From 9b1c0e006f20091f28f3f468cfcab1feb51286bd Mon Sep 17 00:00:00 2001 From: Neo2003 Date: Thu, 2 Oct 2008 16:23:55 -0500 Subject: [svn] * Proper SVN structure --HG-- branch : trunk --- src/Makefile.am | 23 + src/bindings/Makefile.am | 23 + src/bindings/interface/Makefile.am | 52 + src/bindings/interface/Readme.txt | 32 + src/bindings/interface/ScriptMgr.cpp | 328 + src/bindings/interface/ScriptMgr.h | 159 + src/bindings/interface/Scripts/sc_default.cpp | 119 + src/bindings/interface/Scripts/sc_defines.cpp | 152 + src/bindings/interface/Scripts/sc_defines.h | 96 + src/bindings/interface/config.h | 31 + src/bindings/interface/system.cpp | 25 + src/bindings/scripts/How to install.txt | 32 + src/bindings/scripts/LICENSE.txt | 280 + src/bindings/scripts/Makefile.am | 421 + src/bindings/scripts/ScriptMgr.cpp | 2113 +++ src/bindings/scripts/ScriptMgr.h | 94 + src/bindings/scripts/VC71/71ScriptDev2.vcproj | 1598 ++ src/bindings/scripts/VC80/80ScriptDev2.vcproj | 2347 +++ src/bindings/scripts/VC90/90ScriptDev2.vcproj | 2345 +++ src/bindings/scripts/VCProjToLinuxMake.exe | Bin 0 -> 32768 bytes src/bindings/scripts/docs/EventAI.txt | 701 + src/bindings/scripts/docs/How to install.txt | 32 + src/bindings/scripts/docs/LICENSE.txt | 280 + src/bindings/scripts/docs/Script Layout.txt | 32 + src/bindings/scripts/docs/ToDo.txt | 14 + src/bindings/scripts/include/precompiled.cpp | 5 + src/bindings/scripts/include/precompiled.h | 25 + src/bindings/scripts/include/sc_creature.cpp | 612 + src/bindings/scripts/include/sc_creature.h | 196 + src/bindings/scripts/include/sc_gossip.h | 183 + src/bindings/scripts/include/sc_grid_searchers.h | 131 + src/bindings/scripts/include/sc_instance.h | 48 + .../scripts/patches/MaNGOS-r4106-ScriptDev2.patch | 40 + .../scripts/patches/MaNGOS-r4203-ScriptDev2.patch | 40 + .../scripts/patches/MaNGOS-r4241-ScriptDev2.patch | 40 + .../scripts/patches/MaNGOS-r4798-ScriptDev2.patch | 40 + .../scripts/patches/MaNGOS-r5049-Scriptdev2.patch | 42 + .../scripts/patches/MaNGOS-r6491-Scriptdev2.patch | 41 + .../scripts/patches/MaNGOS-r6657-Scriptdev2.patch | 42 + .../scripts/areatrigger/areatrigger_scripts.cpp | 44 + src/bindings/scripts/scripts/boss/boss_emeriss.cpp | 156 + src/bindings/scripts/scripts/boss/boss_lethon.cpp | 24 + src/bindings/scripts/scripts/boss/boss_taerar.cpp | 306 + src/bindings/scripts/scripts/boss/boss_ysondre.cpp | 246 + .../scripts/scripts/creature/mob_event_ai.cpp | 1389 ++ .../scripts/scripts/creature/mob_event_ai.h | 215 + .../scripts/creature/mob_generic_creature.cpp | 172 + .../scripts/scripts/creature/simple_ai.cpp | 294 + src/bindings/scripts/scripts/creature/simple_ai.h | 74 + .../scripts/scripts/custom/custom_example.cpp | 277 + .../scripts/custom/custom_gossip_codebox.cpp | 81 + src/bindings/scripts/scripts/custom/test.cpp | 202 + src/bindings/scripts/scripts/go/go_scripts.cpp | 209 + src/bindings/scripts/scripts/guard/guard_ai.cpp | 160 + src/bindings/scripts/scripts/guard/guard_ai.h | 25 + src/bindings/scripts/scripts/guard/guards.cpp | 4117 +++++ src/bindings/scripts/scripts/item/item_scripts.cpp | 529 + src/bindings/scripts/scripts/item/item_test.cpp | 42 + src/bindings/scripts/scripts/npc/npc_escortAI.cpp | 304 + src/bindings/scripts/scripts/npc/npc_escortAI.h | 85 + src/bindings/scripts/scripts/npc/npc_innkeeper.cpp | 144 + .../scripts/scripts/npc/npc_professions.cpp | 1205 ++ src/bindings/scripts/scripts/npc/npcs_special.cpp | 878 + .../zone/alterac_mountains/alterac_mountains.cpp | 62 + .../auchenai_crypts/boss_exarch_maladaar.cpp | 405 + .../mana_tombs/boss_nexusprince_shaffar.cpp | 313 + .../aunchindoun/mana_tombs/boss_pandemonius.cpp | 159 + .../sethekk_halls/boss_darkweaver_syth.cpp | 441 + .../sethekk_halls/boss_tailonking_ikiss.cpp | 256 + .../aunchindoun/sethekk_halls/def_sethekk_halls.h | 9 + .../sethekk_halls/instance_sethekk_halls.cpp | 74 + .../shadow_labyrinth/boss_ambassador_hellmaw.cpp | 223 + .../boss_blackheart_the_inciter.cpp | 191 + .../shadow_labyrinth/boss_grandmaster_vorpil.cpp | 294 + .../aunchindoun/shadow_labyrinth/boss_murmur.cpp | 192 + .../shadow_labyrinth/def_shadow_labyrinth.h | 13 + .../shadow_labyrinth/instance_shadow_labyrinth.cpp | 168 + .../scripts/scripts/zone/azshara/azshara.cpp | 164 + .../scripts/scripts/zone/azshara/boss_azuregos.cpp | 153 + .../scripts/zone/azuremyst_isle/azuremyst_isle.cpp | 372 + .../scripts/scripts/zone/barrens/the_barrens.cpp | 189 + .../scripts/zone/black_temple/black_temple.cpp | 68 + .../scripts/zone/black_temple/boss_bloodboil.cpp | 365 + .../scripts/zone/black_temple/boss_illidan.cpp | 2465 +++ .../zone/black_temple/boss_mother_shahraz.cpp | 353 + .../zone/black_temple/boss_reliquary_of_souls.cpp | 1051 ++ .../zone/black_temple/boss_shade_of_akama.cpp | 813 + .../scripts/zone/black_temple/boss_supremus.cpp | 406 + .../zone/black_temple/boss_teron_gorefiend.cpp | 589 + .../zone/black_temple/boss_warlord_najentus.cpp | 435 + .../scripts/zone/black_temple/def_black_temple.h | 34 + .../scripts/zone/black_temple/illidari_council.cpp | 885 + .../zone/black_temple/instance_black_temple.cpp | 252 + .../zone/blackrock_depths/blackrock_depths.cpp | 237 + .../blackrock_depths/boss_ambassador_flamelash.cpp | 106 + .../zone/blackrock_depths/boss_angerrel.cpp | 91 + .../zone/blackrock_depths/boss_anubshiah.cpp | 115 + .../scripts/zone/blackrock_depths/boss_doomrel.cpp | 139 + .../scripts/zone/blackrock_depths/boss_doperel.cpp | 91 + .../boss_emperor_dagran_thaurissan.cpp | 104 + .../blackrock_depths/boss_general_angerforge.cpp | 167 + .../zone/blackrock_depths/boss_gloomrel.cpp | 142 + .../blackrock_depths/boss_gorosh_the_dervish.cpp | 81 + .../scripts/zone/blackrock_depths/boss_grizzle.cpp | 86 + .../scripts/zone/blackrock_depths/boss_haterel.cpp | 105 + .../boss_high_interrogator_gerstahn.cpp | 105 + .../scripts/zone/blackrock_depths/boss_magmus.cpp | 84 + .../blackrock_depths/boss_moira_bronzebeard.cpp | 99 + .../zone/blackrock_depths/boss_seethrel.cpp | 115 + .../scripts/zone/blackrock_depths/boss_vilerel.cpp | 101 + .../zone/blackrock_spire/boss_drakkisath.cpp | 101 + .../scripts/zone/blackrock_spire/boss_gyth.cpp | 205 + .../scripts/zone/blackrock_spire/boss_halycon.cpp | 95 + .../zone/blackrock_spire/boss_highlord_omokk.cpp | 131 + .../blackrock_spire/boss_mother_smolderweb.cpp | 86 + .../blackrock_spire/boss_overlord_wyrmthalak.cpp | 127 + .../blackrock_spire/boss_pyroguard_emberseer.cpp | 93 + .../blackrock_spire/boss_quartermaster_zigris.cpp | 85 + .../zone/blackrock_spire/boss_rend_blackhand.cpp | 91 + .../boss_shadow_hunter_voshgajin.cpp | 95 + .../zone/blackrock_spire/boss_the_beast.cpp | 93 + .../zone/blackrock_spire/boss_warmaster_voone.cpp | 121 + .../blackwing_lair/boss_broodlord_lashlayer.cpp | 130 + .../zone/blackwing_lair/boss_chromaggus.cpp | 320 + .../scripts/zone/blackwing_lair/boss_ebonroc.cpp | 103 + .../scripts/zone/blackwing_lair/boss_firemaw.cpp | 94 + .../scripts/zone/blackwing_lair/boss_flamegor.cpp | 94 + .../scripts/zone/blackwing_lair/boss_nefarian.cpp | 250 + .../scripts/zone/blackwing_lair/boss_razorgore.cpp | 126 + .../zone/blackwing_lair/boss_vaelastrasz.cpp | 278 + .../zone/blackwing_lair/boss_victor_nefarius.cpp | 399 + .../blackwing_lair/instance_blackwing_lair.cpp | 8 + .../blades_edge_mountains.cpp | 441 + .../scripts/zone/blasted_lands/blasted_lands.cpp | 159 + .../scripts/zone/blasted_lands/boss_kruul.cpp | 182 + .../scripts/zone/bloodmyst_isle/bloodmyst_isle.cpp | 141 + .../zone/burning_steppes/burning_steppes.cpp | 157 + .../caverns_of_time/dark_portal/boss_aeonus.cpp | 137 + .../dark_portal/boss_chrono_lord_deja.cpp | 124 + .../caverns_of_time/dark_portal/boss_temporus.cpp | 166 + .../zone/caverns_of_time/hyjal/boss_archimonde.cpp | 788 + .../scripts/zone/caverns_of_time/hyjal/def_hyjal.h | 25 + .../scripts/zone/caverns_of_time/hyjal/hyjal.cpp | 217 + .../scripts/zone/caverns_of_time/hyjal/hyjalAI.cpp | 458 + .../scripts/zone/caverns_of_time/hyjal/hyjalAI.h | 286 + .../zone/caverns_of_time/hyjal/instance_hyjal.cpp | 203 + .../old_hillsbrad/boss_captain_skarloc.cpp | 177 + .../old_hillsbrad/boss_epoch_hunter.cpp | 183 + .../old_hillsbrad/boss_leutenant_drake.cpp | 246 + .../old_hillsbrad/def_old_hillsbrad.h | 16 + .../old_hillsbrad/instance_old_hillsbrad.cpp | 178 + .../old_hillsbrad/old_hillsbrad.cpp | 916 + .../serpent_shrine/boss_fathomlord_karathress.cpp | 559 + .../serpent_shrine/boss_hydross_the_unstable.cpp | 357 + .../serpent_shrine/boss_lady_vashj.cpp | 940 + .../serpent_shrine/boss_leotheras_the_blind.cpp | 359 + .../serpent_shrine/boss_morogrim_tidewalker.cpp | 406 + .../serpent_shrine/def_serpent_shrine.h | 26 + .../serpent_shrine/instance_serpent_shrine.cpp | 227 + .../coilfang_resevoir/slave_pens/boss_rokmar.cpp | 65 + .../steam_vault/boss_hydromancer_thespia.cpp | 223 + .../steam_vault/boss_mekgineer_steamrigger.cpp | 297 + .../steam_vault/boss_warlord_kalithresh.cpp | 255 + .../steam_vault/def_steam_vault.h | 16 + .../steam_vault/instance_steam_vault.cpp | 170 + .../coilfang_resevoir/underbog/boss_ghazan.cpp | 78 + .../coilfang_resevoir/underbog/boss_hungarfen.cpp | 156 + .../scripts/scripts/zone/darkshore/darkshore.cpp | 24 + .../scripts/scripts/zone/deadmines/deadmines.cpp | 24 + .../scripts/zone/deadmines/instance_deadmines.cpp | 22 + .../scripts/scripts/zone/dun_morogh/dun_morogh.cpp | 98 + .../zone/dustwallow_marsh/dustwallow_marsh.cpp | 231 + .../eastern_plaguelands/eastern_plaguelands.cpp | 180 + .../scripts/zone/elwynn_forest/elwynn_forest.cpp | 98 + .../scripts/zone/eversong_woods/eversong_woods.cpp | 163 + .../scripts/scripts/zone/felwood/felwood.cpp | 89 + .../scripts/scripts/zone/feralas/feralas.cpp | 85 + .../scripts/scripts/zone/ghostlands/ghostlands.cpp | 134 + .../scripts/zone/gruuls_lair/boss_gruul.cpp | 281 + .../zone/gruuls_lair/boss_high_king_maulgar.cpp | 687 + .../scripts/zone/gruuls_lair/def_gruuls_lair.h | 15 + .../zone/gruuls_lair/instance_gruuls_lair.cpp | 139 + .../blood_furnace/boss_broggok.cpp | 132 + .../blood_furnace/boss_kelidan_the_breaker.cpp | 253 + .../blood_furnace/boss_the_maker.cpp | 160 + .../hellfire_ramparts/boss_omor_the_unscarred.cpp | 277 + .../boss_watchkeeper_gargolmar.cpp | 200 + .../magtheridons_lair/boss_magtheridon.cpp | 437 + .../magtheridons_lair/def_magtheridons_lair.h | 13 + .../instance_magtheridons_lair.cpp | 116 + .../shattered_halls/boss_nethekurse.cpp | 505 + .../shattered_halls/boss_warbringer_omrogg.cpp | 414 + .../shattered_halls/def_shattered_halls.h | 13 + .../shattered_halls/instance_shattered_halls.cpp | 114 + .../hellfire_peninsula/boss_doomlord_kazzak.cpp | 141 + .../zone/hellfire_peninsula/hellfire_peninsula.cpp | 187 + .../scripts/scripts/zone/ironforge/ironforge.cpp | 93 + .../zone/isle_of_queldanas/isle_of_queldanas.cpp | 155 + .../scripts/scripts/zone/karazhan/boss_curator.cpp | 200 + .../zone/karazhan/boss_maiden_of_virtue.cpp | 176 + .../scripts/zone/karazhan/boss_midnight.cpp | 371 + .../scripts/scripts/zone/karazhan/boss_moroes.cpp | 868 + .../scripts/zone/karazhan/boss_netherspite.cpp | 37 + .../scripts/zone/karazhan/boss_nightbane.cpp | 33 + .../zone/karazhan/boss_prince_malchezaar.cpp | 646 + .../scripts/zone/karazhan/boss_shade_of_aran.cpp | 673 + .../zone/karazhan/boss_terestian_illhoof.cpp | 454 + .../scripts/scripts/zone/karazhan/bosses_opera.cpp | 1471 ++ .../scripts/scripts/zone/karazhan/def_karazhan.h | 40 + .../scripts/zone/karazhan/instance_karazhan.cpp | 253 + .../scripts/scripts/zone/karazhan/karazhan.cpp | 470 + .../scripts/scripts/zone/loch_modan/loch_modan.cpp | 91 + .../magisters_terrace/boss_felblood_kaelthas.cpp | 591 + .../magisters_terrace/boss_priestess_delrissa.cpp | 1367 ++ .../magisters_terrace/boss_selin_fireheart.cpp | 385 + .../zone/magisters_terrace/boss_vexallus.cpp | 233 + .../zone/magisters_terrace/def_magisters_terrace.h | 29 + .../instance_magisters_terrace.cpp | 195 + .../zone/maraudon/boss_celebras_the_cursed.cpp | 97 + .../scripts/zone/maraudon/boss_landslide.cpp | 94 + .../scripts/scripts/zone/maraudon/boss_noxxion.cpp | 149 + .../zone/maraudon/boss_princess_theradras.cpp | 108 + .../scripts/zone/molten_core/boss_baron_geddon.cpp | 105 + .../scripts/scripts/zone/molten_core/boss_garr.cpp | 168 + .../scripts/zone/molten_core/boss_gehennas.cpp | 91 + .../scripts/zone/molten_core/boss_golemagg.cpp | 219 + .../scripts/zone/molten_core/boss_lucifron.cpp | 90 + .../scripts/zone/molten_core/boss_magmadar.cpp | 97 + .../zone/molten_core/boss_majordomo_executus.cpp | 133 + .../scripts/zone/molten_core/boss_ragnaros.cpp | 317 + .../scripts/zone/molten_core/boss_shazzrah.cpp | 121 + .../zone/molten_core/boss_sulfuron_harbinger.cpp | 215 + .../scripts/zone/molten_core/def_molten_core.h | 21 + .../zone/molten_core/instance_molten_core.cpp | 260 + .../scripts/zone/molten_core/molten_core.cpp | 88 + .../scripts/scripts/zone/moonglade/moonglade.cpp | 217 + .../scripts/scripts/zone/mulgore/mulgore.cpp | 64 + .../scripts/scripts/zone/nagrand/nagrand.cpp | 568 + .../scripts/zone/naxxramas/boss_anubrekhan.cpp | 215 + .../scripts/zone/naxxramas/boss_faerlina.cpp | 202 + .../scripts/scripts/zone/naxxramas/boss_feugen.cpp | 33 + .../scripts/scripts/zone/naxxramas/boss_gluth.cpp | 174 + .../scripts/scripts/zone/naxxramas/boss_gothik.cpp | 62 + .../scripts/zone/naxxramas/boss_grobbulus.cpp | 30 + .../scripts/scripts/zone/naxxramas/boss_heigan.cpp | 46 + .../zone/naxxramas/boss_highlord_mograine.cpp | 178 + .../scripts/zone/naxxramas/boss_kelthuzad.cpp | 542 + .../scripts/zone/naxxramas/boss_lady_blaumeux.cpp | 147 + .../scripts/zone/naxxramas/boss_loatheb.cpp | 216 + .../scripts/zone/naxxramas/boss_maexxna.cpp | 246 + .../scripts/scripts/zone/naxxramas/boss_noth.cpp | 180 + .../scripts/zone/naxxramas/boss_patchwerk.cpp | 160 + .../scripts/zone/naxxramas/boss_razuvious.cpp | 167 + .../scripts/zone/naxxramas/boss_sapphiron.cpp | 199 + .../scripts/zone/naxxramas/boss_sir_zeliek.cpp | 146 + .../scripts/zone/naxxramas/boss_stalagg.cpp | 35 + .../scripts/zone/naxxramas/boss_thaddius.cpp | 49 + .../scripts/zone/naxxramas/boss_thane_korthazz.cpp | 147 + .../scripts/zone/naxxramas/instance_naxxramas.cpp | 24 + .../scripts/zone/netherstorm/netherstorm.cpp | 423 + .../scripts/zone/onyxias_lair/boss_onyxia.cpp | 229 + .../scripts/scripts/zone/orgrimmar/orgrimmar.cpp | 265 + .../boss_amnennar_the_coldbringer.cpp | 140 + .../zone/ruins_of_ahnqiraj/boss_ayamiss.cpp | 107 + .../scripts/zone/ruins_of_ahnqiraj/boss_buru.cpp | 24 + .../zone/ruins_of_ahnqiraj/boss_kurinnaxx.cpp | 93 + .../scripts/zone/ruins_of_ahnqiraj/boss_moam.cpp | 117 + .../zone/ruins_of_ahnqiraj/boss_ossirian.cpp | 36 + .../scripts/zone/ruins_of_ahnqiraj/boss_rajaxx.cpp | 42 + .../instance_ruins_of_ahnqiraj.cpp | 24 + .../zone/scarlet_monastery/boss_arcanist_doan.cpp | 171 + .../boss_azshir_the_sleepless.cpp | 97 + .../scarlet_monastery/boss_bloodmage_thalnos.cpp | 136 + .../scripts/zone/scarlet_monastery/boss_herod.cpp | 197 + .../boss_high_inquisitor_fairbanks.cpp | 132 + .../boss_high_inquisitor_whitemane.cpp | 177 + .../scarlet_monastery/boss_houndmaster_loksey.cpp | 78 + .../scarlet_monastery/boss_interrogator_vishas.cpp | 113 + .../boss_scarlet_commander_mograine.cpp | 160 + .../scripts/zone/scarlet_monastery/boss_scorn.cpp | 100 + .../zone/scholomance/boss_darkmaster_gandling.cpp | 192 + .../scholomance/boss_death_knight_darkreaver.cpp | 59 + .../scholomance/boss_doctor_theolen_krastinov.cpp | 108 + .../zone/scholomance/boss_illucia_barov.cpp | 116 + .../zone/scholomance/boss_instructor_malicia.cpp | 152 + .../zone/scholomance/boss_jandice_barov.cpp | 226 + .../scripts/zone/scholomance/boss_kormok.cpp | 155 + .../zone/scholomance/boss_lord_alexei_barov.cpp | 98 + .../zone/scholomance/boss_lorekeeper_polkelt.cpp | 113 + .../zone/scholomance/boss_ras_frostwhisper.cpp | 125 + .../scripts/zone/scholomance/boss_the_ravenian.cpp | 118 + .../scripts/zone/scholomance/boss_vectus.cpp | 95 + .../scripts/zone/scholomance/def_scholomance.h | 15 + .../zone/scholomance/instance_scholomance.cpp | 102 + .../scripts/zone/searing_gorge/searing_gorge.cpp | 159 + .../zone/shadowfang_keep/def_shadowfang_keep.h | 12 + .../shadowfang_keep/instance_shadowfang_keep.cpp | 152 + .../zone/shadowfang_keep/shadowfang_keep.cpp | 117 + .../zone/shadowmoon_valley/boss_doomwalker.cpp | 211 + .../zone/shadowmoon_valley/shadowmoon_valley.cpp | 748 + .../scripts/zone/shattrath/shattrath_city.cpp | 268 + .../scripts/scripts/zone/silithus/silithus.cpp | 149 + .../scripts/zone/silvermoon/silvermoon_city.cpp | 103 + .../zone/silverpine_forest/silverpine_forest.cpp | 100 + .../stonetalon_mountains/stonetalon_mountains.cpp | 80 + .../scripts/zone/stormwind/stormwind_city.cpp | 272 + .../zone/stranglethorn_vale/stranglethorn_vale.cpp | 107 + .../zone/stratholme/boss_baron_rivendare.cpp | 215 + .../zone/stratholme/boss_baroness_anastari.cpp | 124 + .../zone/stratholme/boss_cannon_master_willey.cpp | 221 + .../zone/stratholme/boss_dathrohan_balnazzar.cpp | 316 + .../zone/stratholme/boss_magistrate_barthilas.cpp | 114 + .../zone/stratholme/boss_maleki_the_pallid.cpp | 119 + .../scripts/zone/stratholme/boss_nerubenkan.cpp | 138 + .../zone/stratholme/boss_order_of_silver_hand.cpp | 161 + .../zone/stratholme/boss_postmaster_malown.cpp | 144 + .../zone/stratholme/boss_ramstein_the_gorger.cpp | 92 + .../zone/stratholme/boss_timmy_the_cruel.cpp | 103 + .../scripts/zone/stratholme/def_stratholme.h | 14 + .../zone/stratholme/instance_stratholme.cpp | 77 + .../scripts/scripts/zone/stratholme/stratholme.cpp | 335 + .../zone/sunwell_plateau/boss_brutallus.cpp | 188 + .../scripts/zone/sunwell_plateau/boss_kalecgos.cpp | 657 + .../zone/sunwell_plateau/def_sunwell_plateau.h | 43 + .../sunwell_plateau/instance_sunwell_plateau.cpp | 275 + .../scripts/scripts/zone/tanaris/tanaris.cpp | 402 + .../zone/tempest_keep/arcatraz/arcatraz.cpp | 612 + .../arcatraz/boss_harbinger_skyriss.cpp | 380 + .../zone/tempest_keep/arcatraz/def_arcatraz.h | 19 + .../tempest_keep/arcatraz/instance_arcatraz.cpp | 224 + .../botanica/boss_high_botanist_freywinn.cpp | 215 + .../zone/tempest_keep/botanica/boss_laj.cpp | 198 + .../tempest_keep/botanica/boss_warp_splinter.cpp | 288 + .../zone/tempest_keep/the_eye/boss_astromancer.cpp | 563 + .../zone/tempest_keep/the_eye/boss_kaelthas.cpp | 1639 ++ .../zone/tempest_keep/the_eye/boss_void_reaver.cpp | 203 + .../zone/tempest_keep/the_eye/def_the_eye.h | 20 + .../zone/tempest_keep/the_eye/instance_the_eye.cpp | 178 + .../scripts/zone/tempest_keep/the_eye/the_eye.cpp | 98 + .../the_mechanar/boss_gatewatcher_gyrokill.cpp | 24 + .../the_mechanar/boss_gatewatcher_ironhand.cpp | 147 + .../the_mechanar/boss_nethermancer_sepethrea.cpp | 223 + .../zone/temple_of_ahnqiraj/boss_bug_trio.cpp | 384 + .../scripts/zone/temple_of_ahnqiraj/boss_cthun.cpp | 1360 ++ .../zone/temple_of_ahnqiraj/boss_fankriss.cpp | 190 + .../zone/temple_of_ahnqiraj/boss_huhuran.cpp | 143 + .../scripts/zone/temple_of_ahnqiraj/boss_ouro.cpp | 140 + .../zone/temple_of_ahnqiraj/boss_sartura.cpp | 271 + .../zone/temple_of_ahnqiraj/boss_skeram.cpp | 314 + .../zone/temple_of_ahnqiraj/boss_twinemperors.cpp | 698 + .../zone/temple_of_ahnqiraj/boss_viscidus.cpp | 29 + .../temple_of_ahnqiraj/def_temple_of_ahnqiraj.h | 22 + .../instance_temple_of_ahnqiraj.cpp | 165 + .../temple_of_ahnqiraj/mob_anubisath_sentinel.cpp | 344 + .../zone/terokkar_forest/terokkar_forest.cpp | 399 + .../scripts/zone/thunder_bluff/thunder_bluff.cpp | 136 + .../zone/tirisfal_glades/tirisfal_glades.cpp | 88 + .../scripts/scripts/zone/uldaman/boss_ironaya.cpp | 107 + .../scripts/scripts/zone/uldaman/uldaman.cpp | 187 + .../scripts/scripts/zone/undercity/undercity.cpp | 260 + .../wailing_caverns/instance_wailing_caverns.cpp | 24 + .../western_plaguelands/western_plaguelands.cpp | 176 + .../scripts/zone/winterspring/winterspring.cpp | 157 + .../scripts/zone/zangarmarsh/zangarmarsh.cpp | 283 + .../scripts/scripts/zone/zulaman/boss_janalai.cpp | 777 + .../scripts/scripts/zone/zulaman/boss_nalorakk.cpp | 272 + .../scripts/scripts/zone/zulaman/def_zulaman.h | 17 + .../scripts/zone/zulaman/instance_zulaman.cpp | 137 + .../scripts/scripts/zone/zulaman/zulaman.cpp | 108 + .../scripts/scripts/zone/zulfarrak/zulfarrak.cpp | 224 + .../scripts/scripts/zone/zulgurub/boss_arlokk.cpp | 211 + .../scripts/zone/zulgurub/boss_gahzranka.cpp | 92 + .../scripts/scripts/zone/zulgurub/boss_grilek.cpp | 92 + .../scripts/scripts/zone/zulgurub/boss_hakkar.cpp | 256 + .../scripts/zone/zulgurub/boss_hazzarah.cpp | 100 + .../scripts/scripts/zone/zulgurub/boss_jeklik.cpp | 297 + .../scripts/scripts/zone/zulgurub/boss_jindo.cpp | 308 + .../scripts/zone/zulgurub/boss_mandokir.cpp | 311 + .../scripts/scripts/zone/zulgurub/boss_marli.cpp | 269 + .../scripts/zone/zulgurub/boss_renataki.cpp | 151 + .../scripts/scripts/zone/zulgurub/boss_thekal.cpp | 545 + .../scripts/scripts/zone/zulgurub/boss_venoxis.cpp | 200 + .../scripts/zone/zulgurub/boss_wushoolay.cpp | 84 + .../scripts/scripts/zone/zulgurub/def_zulgurub.h | 36 + .../scripts/zone/zulgurub/instance_zulgurub.cpp | 238 + src/bindings/scripts/sql/Makefile.am | 31 + src/bindings/scripts/sql/Makefile.in | 540 + .../scripts/sql/Updates/09_BraveWindfeather.sql | 1 + .../scripts/sql/Updates/11_SilvaFilnaveth.sql | 1 + src/bindings/scripts/sql/Updates/27_Vaelastraz.sql | 2 + src/bindings/scripts/sql/Updates/Makefile.am | 215 + src/bindings/scripts/sql/Updates/Makefile.in | 383 + src/bindings/scripts/sql/Updates/r104.sql | 1 + src/bindings/scripts/sql/Updates/r108.sql | 1 + src/bindings/scripts/sql/Updates/r110.sql | 1 + src/bindings/scripts/sql/Updates/r121.sql | 2 + src/bindings/scripts/sql/Updates/r123.sql | 4 + src/bindings/scripts/sql/Updates/r124.sql | 13 + src/bindings/scripts/sql/Updates/r125.sql | 24 + src/bindings/scripts/sql/Updates/r128.sql | 1 + src/bindings/scripts/sql/Updates/r131.sql | 1 + src/bindings/scripts/sql/Updates/r134.sql | 15 + src/bindings/scripts/sql/Updates/r136.sql | 55 + src/bindings/scripts/sql/Updates/r139.sql | 35 + src/bindings/scripts/sql/Updates/r142.sql | 11 + src/bindings/scripts/sql/Updates/r144.sql | 5 + src/bindings/scripts/sql/Updates/r145.sql | 4 + src/bindings/scripts/sql/Updates/r149.sql | 1 + src/bindings/scripts/sql/Updates/r150.sql | 19 + src/bindings/scripts/sql/Updates/r152.sql | 12 + src/bindings/scripts/sql/Updates/r153.sql | 33 + src/bindings/scripts/sql/Updates/r157.sql | 7 + src/bindings/scripts/sql/Updates/r161.sql | 12 + src/bindings/scripts/sql/Updates/r163.sql | 2 + src/bindings/scripts/sql/Updates/r164.sql | 10 + src/bindings/scripts/sql/Updates/r165.sql | 8 + src/bindings/scripts/sql/Updates/r169.sql | 4 + src/bindings/scripts/sql/Updates/r170.sql | 2 + src/bindings/scripts/sql/Updates/r171.sql | 2 + src/bindings/scripts/sql/Updates/r172.sql | 3 + src/bindings/scripts/sql/Updates/r174.sql | 8 + src/bindings/scripts/sql/Updates/r176.sql | 5 + src/bindings/scripts/sql/Updates/r177.sql | 2 + src/bindings/scripts/sql/Updates/r178.sql | 2 + src/bindings/scripts/sql/Updates/r181.sql | 2 + src/bindings/scripts/sql/Updates/r182.sql | 2 + src/bindings/scripts/sql/Updates/r183.sql | 5 + src/bindings/scripts/sql/Updates/r184.sql | 2 + src/bindings/scripts/sql/Updates/r186.sql | 2 + src/bindings/scripts/sql/Updates/r187.sql | 12 + src/bindings/scripts/sql/Updates/r189.sql | 3 + src/bindings/scripts/sql/Updates/r191.sql | 5 + src/bindings/scripts/sql/Updates/r192.sql | 9 + src/bindings/scripts/sql/Updates/r197.sql | 5 + src/bindings/scripts/sql/Updates/r204.sql | 7 + src/bindings/scripts/sql/Updates/r206.sql | 16 + src/bindings/scripts/sql/Updates/r211.sql | 6 + src/bindings/scripts/sql/Updates/r213.sql | 11 + src/bindings/scripts/sql/Updates/r214.sql | 2 + src/bindings/scripts/sql/Updates/r215.sql | 1 + src/bindings/scripts/sql/Updates/r218.sql | 5 + src/bindings/scripts/sql/Updates/r219.sql | 3 + .../scripts/sql/Updates/r221_scriptdev2.sql | 1 + .../scripts/sql/Updates/r227_scriptdev2.sql | 29 + src/bindings/scripts/sql/Updates/r230_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r234_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r237_mangos.sql | 6 + src/bindings/scripts/sql/Updates/r238_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r239_mangos.sql | 1 + .../scripts/sql/Updates/r240_scriptdev2.sql | 2 + src/bindings/scripts/sql/Updates/r241_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r242_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r243_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r249_mangos.sql | 3 + src/bindings/scripts/sql/Updates/r250_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r253_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r255_mangos.sql | 5 + src/bindings/scripts/sql/Updates/r256_mangos.sql | 35 + src/bindings/scripts/sql/Updates/r257_mangos.sql | 4 + src/bindings/scripts/sql/Updates/r258_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r260_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r261_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r262_mangos.sql | 3 + src/bindings/scripts/sql/Updates/r263_mangos.sql | 3 + src/bindings/scripts/sql/Updates/r264_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r265_mangos.sql | 9 + src/bindings/scripts/sql/Updates/r269_mangos.sql | 6 + src/bindings/scripts/sql/Updates/r270_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r271_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r272_mangos.sql | 8 + src/bindings/scripts/sql/Updates/r273_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r274_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r275_mangos.sql | 1 + .../scripts/sql/Updates/r281_scriptdev2.sql | 35 + src/bindings/scripts/sql/Updates/r282_mangos.sql | 38 + src/bindings/scripts/sql/Updates/r286_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r289_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r291_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r295_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r297_mangos.sql | 1 + .../scripts/sql/Updates/r298_scriptdev2.sql | 2 + .../scripts/sql/Updates/r299_scriptdev2.sql | 9 + src/bindings/scripts/sql/Updates/r304_mangos.sql | 1 + .../scripts/sql/Updates/r306_scriptdev2.sql | 4 + src/bindings/scripts/sql/Updates/r307_mangos.sql | 4 + src/bindings/scripts/sql/Updates/r308_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r309_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r311_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r312_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r318_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r324_mangos.sql | 3 + src/bindings/scripts/sql/Updates/r327_mangos.sql | 5 + .../scripts/sql/Updates/r332_scriptdev2.sql | 2 + src/bindings/scripts/sql/Updates/r333_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r334_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r336_mangos.sql | 9 + src/bindings/scripts/sql/Updates/r352_mangos.sql | 23 + src/bindings/scripts/sql/Updates/r355_mangos.sql | 23 + src/bindings/scripts/sql/Updates/r358_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r364_mangos.sql | 7 + src/bindings/scripts/sql/Updates/r367_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r368_mangos.sql | 5 + src/bindings/scripts/sql/Updates/r369_mangos.sql | 5 + src/bindings/scripts/sql/Updates/r374_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r386_mangos.sql | 7 + src/bindings/scripts/sql/Updates/r417_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r428_mangos.sql | 4 + src/bindings/scripts/sql/Updates/r431_mangos.sql | 4 + src/bindings/scripts/sql/Updates/r444_mangos.sql | 5 + src/bindings/scripts/sql/Updates/r445_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r446_mangos.sql | 2 + .../scripts/sql/Updates/r448_scriptdev2.sql | 5 + src/bindings/scripts/sql/Updates/r462_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r465_mangos.sql | 3 + src/bindings/scripts/sql/Updates/r467_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r473_mangos.sql | 3 + src/bindings/scripts/sql/Updates/r476_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r477_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r479_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r482_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r484_mangos.sql | 8 + src/bindings/scripts/sql/Updates/r486_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r487_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r494_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r501_mangos.sql | 4 + src/bindings/scripts/sql/Updates/r513_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r514_mangos.sql | 3 + src/bindings/scripts/sql/Updates/r515_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r516_mangos.sql | 3 + src/bindings/scripts/sql/Updates/r517_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r518_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r519_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r520_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r521_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r522_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r526_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r528_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r533_mangos.sql | 1 + .../scripts/sql/Updates/r538_scriptdev2.sql | 60 + src/bindings/scripts/sql/Updates/r547_mangos.sql | 22 + src/bindings/scripts/sql/Updates/r554_mangos.sql | 3 + src/bindings/scripts/sql/Updates/r555_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r56.sql | 2 + src/bindings/scripts/sql/Updates/r575_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r576_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r578_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r584_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r59.sql | 3 + src/bindings/scripts/sql/Updates/r590_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r591_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r593_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r594_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r596_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r610_mangos.sql | 2 + .../scripts/sql/Updates/r615_scriptdev2.sql | 4 + src/bindings/scripts/sql/Updates/r617_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r621_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r628_mangos.sql | 3 + src/bindings/scripts/sql/Updates/r63.sql | 1 + src/bindings/scripts/sql/Updates/r632_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r633_mangos.sql | 1 + .../scripts/sql/Updates/r634_scriptdev2.sql | 35 + src/bindings/scripts/sql/Updates/r636_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r637_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r638_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r639_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r642_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r643_mangos.sql | 2 + src/bindings/scripts/sql/Updates/r646_mangos.sql | 1 + .../scripts/sql/Updates/r647_scriptdev2.sql | 10 + src/bindings/scripts/sql/Updates/r65.sql | 2 + .../scripts/sql/Updates/r656_scriptdev2.sql | 5 + src/bindings/scripts/sql/Updates/r658_mangos.sql | 1 + src/bindings/scripts/sql/Updates/r659_mangos.sql | 4 + src/bindings/scripts/sql/Updates/r72.sql | 4 + src/bindings/scripts/sql/Updates/r78.sql | 3 + src/bindings/scripts/sql/Updates/r81.sql | 35 + src/bindings/scripts/sql/Updates/r91.sql | 2 + src/bindings/scripts/sql/Updates/r92.sql | 1 + src/bindings/scripts/sql/Updates/r97.sql | 1 + src/bindings/scripts/sql/create_database.sql | 4 + src/bindings/scripts/sql/mangos_full_scripts.sql | 871 + src/bindings/scripts/sql/old/mangos_old_spells.sql | 374 + .../optional/mangos_optional_generic_creature.sql | 283 + src/bindings/scripts/sql/scriptdev2_structure.sql | 105 + src/bindings/scripts/system.cpp | 19 + src/bindings/scripts/trinityscript.conf.dist | 20 + src/framework/Dynamic/FactoryHolder.h | 60 + src/framework/Dynamic/ObjectRegistry.h | 107 + src/framework/GameSystem/Grid.h | 129 + src/framework/GameSystem/GridLoader.h | 76 + src/framework/GameSystem/GridRefManager.h | 41 + src/framework/GameSystem/GridReference.h | 52 + src/framework/GameSystem/NGrid.h | 160 + src/framework/GameSystem/TypeContainer.h | 128 + src/framework/GameSystem/TypeContainerFunctions.h | 172 + .../GameSystem/TypeContainerFunctionsPtr.h | 167 + src/framework/GameSystem/TypeContainerVisitor.h | 116 + src/framework/Makefile.am | 64 + src/framework/Network/SocketDefines.h | 46 + src/framework/Platform/CompilerDefs.h | 61 + src/framework/Platform/Define.h | 229 + src/framework/Policies/CreationPolicy.h | 107 + src/framework/Policies/ObjectLifeTime.cpp | 33 + src/framework/Policies/ObjectLifeTime.h | 50 + src/framework/Policies/Singleton.h | 62 + src/framework/Policies/SingletonImp.h | 90 + src/framework/Policies/ThreadingModel.h | 127 + src/framework/Utilities/ByteConverter.h | 57 + src/framework/Utilities/Callback.h | 382 + .../Utilities/CountedReference/Reference.h | 97 + .../Utilities/CountedReference/ReferenceHolder.h | 39 + .../Utilities/CountedReference/ReferenceImpl.h | 130 + src/framework/Utilities/EventProcessor.cpp | 88 + src/framework/Utilities/EventProcessor.h | 68 + src/framework/Utilities/HashMap.h | 63 + src/framework/Utilities/LinkedList.h | 216 + .../Utilities/LinkedReference/RefManager.h | 53 + .../Utilities/LinkedReference/Reference.h | 84 + src/framework/Utilities/TypeList.h | 43 + src/game/AccountMgr.cpp | 195 + src/game/AccountMgr.h | 56 + src/game/AddonHandler.cpp | 208 + src/game/AddonHandler.h | 37 + src/game/AggressorAI.cpp | 155 + src/game/AggressorAI.h | 53 + src/game/AnimalRandomMovementGenerator.h | 28 + src/game/ArenaTeam.cpp | 517 + src/game/ArenaTeam.h | 175 + src/game/ArenaTeamHandler.cpp | 462 + src/game/AuctionHouse.cpp | 747 + src/game/AuctionHouseObject.h | 104 + src/game/Bag.cpp | 249 + src/game/Bag.h | 74 + src/game/BattleGround.cpp | 1153 ++ src/game/BattleGround.h | 486 + src/game/BattleGroundAA.cpp | 63 + src/game/BattleGroundAA.h | 47 + src/game/BattleGroundAB.cpp | 649 + src/game/BattleGroundAB.h | 284 + src/game/BattleGroundAV.cpp | 120 + src/game/BattleGroundAV.h | 59 + src/game/BattleGroundBE.cpp | 212 + src/game/BattleGroundBE.h | 75 + src/game/BattleGroundEY.cpp | 909 + src/game/BattleGroundEY.h | 372 + src/game/BattleGroundHandler.cpp | 621 + src/game/BattleGroundMgr.cpp | 867 + src/game/BattleGroundMgr.h | 178 + src/game/BattleGroundNA.cpp | 181 + src/game/BattleGroundNA.h | 69 + src/game/BattleGroundRL.cpp | 180 + src/game/BattleGroundRL.h | 65 + src/game/BattleGroundWS.cpp | 678 + src/game/BattleGroundWS.h | 185 + src/game/Cell.h | 162 + src/game/CellImpl.h | 130 + src/game/Channel.cpp | 994 + src/game/Channel.h | 291 + src/game/ChannelHandler.cpp | 387 + src/game/ChannelMgr.h | 101 + src/game/CharacterHandler.cpp | 1065 ++ src/game/Chat.cpp | 1103 ++ src/game/Chat.h | 406 + src/game/ChatHandler.cpp | 583 + src/game/CombatHandler.cpp | 95 + src/game/ConfusedMovementGenerator.cpp | 155 + src/game/ConfusedMovementGenerator.h | 48 + src/game/Corpse.cpp | 211 + src/game/Corpse.h | 100 + src/game/Creature.cpp | 1993 ++ src/game/Creature.h | 611 + src/game/CreatureAI.cpp | 26 + src/game/CreatureAI.h | 121 + src/game/CreatureAIImpl.h | 30 + src/game/CreatureAIRegistry.cpp | 47 + src/game/CreatureAIRegistry.h | 26 + src/game/CreatureAISelector.cpp | 120 + src/game/CreatureAISelector.h | 31 + src/game/DestinationHolder.cpp | 19 + src/game/DestinationHolder.h | 62 + src/game/DestinationHolderImp.h | 243 + src/game/DuelHandler.cpp | 90 + src/game/DynamicObject.cpp | 152 + src/game/DynamicObject.h | 72 + src/game/FleeingMovementGenerator.cpp | 379 + src/game/FleeingMovementGenerator.h | 62 + src/game/FollowerRefManager.h | 31 + src/game/FollowerReference.cpp | 36 + src/game/FollowerReference.h | 34 + src/game/Formulas.h | 198 + src/game/GameEvent.cpp | 659 + src/game/GameEvent.h | 93 + src/game/GameObject.cpp | 1253 ++ src/game/GameObject.h | 592 + src/game/GlobalEvents.cpp | 82 + src/game/GlobalEvents.h | 29 + src/game/GossipDef.cpp | 765 + src/game/GossipDef.h | 193 + src/game/GridDefines.h | 170 + src/game/GridNotifiers.cpp | 218 + src/game/GridNotifiers.h | 812 + src/game/GridNotifiersImpl.h | 489 + src/game/GridStates.cpp | 73 + src/game/GridStates.h | 72 + src/game/Group.cpp | 1406 ++ src/game/Group.h | 368 + src/game/GroupHandler.cpp | 934 + src/game/GroupRefManager.h | 33 + src/game/GroupReference.cpp | 39 + src/game/GroupReference.h | 41 + src/game/GuardAI.cpp | 150 + src/game/GuardAI.h | 54 + src/game/Guild.cpp | 1940 ++ src/game/Guild.h | 435 + src/game/GuildHandler.cpp | 1815 ++ src/game/HateMatrix.h | 84 + src/game/HomeMovementGenerator.cpp | 86 + src/game/HomeMovementGenerator.h | 54 + src/game/HostilRefManager.cpp | 147 + src/game/HostilRefManager.h | 66 + src/game/IdleMovementGenerator.cpp | 49 + src/game/IdleMovementGenerator.h | 52 + src/game/InstanceData.cpp | 29 + src/game/InstanceData.h | 66 + src/game/InstanceSaveMgr.cpp | 649 + src/game/InstanceSaveMgr.h | 172 + src/game/Item.cpp | 915 + src/game/Item.h | 298 + src/game/ItemEnchantmentMgr.cpp | 202 + src/game/ItemEnchantmentMgr.h | 27 + src/game/ItemHandler.cpp | 1221 ++ src/game/ItemPrototype.h | 565 + src/game/LFGHandler.cpp | 337 + src/game/Language.h | 665 + src/game/Level0.cpp | 233 + src/game/Level1.cpp | 2351 +++ src/game/Level2.cpp | 3948 ++++ src/game/Level3.cpp | 5424 ++++++ src/game/LootHandler.cpp | 494 + src/game/LootMgr.cpp | 1239 ++ src/game/LootMgr.h | 331 + src/game/Mail.cpp | 838 + src/game/Mail.h | 212 + src/game/Makefile.am | 278 + src/game/Map.cpp | 1795 ++ src/game/Map.h | 396 + src/game/MapInstanced.cpp | 252 + src/game/MapInstanced.h | 70 + src/game/MapManager.cpp | 340 + src/game/MapManager.h | 141 + src/game/MiscHandler.cpp | 1761 ++ src/game/MotionMaster.cpp | 342 + src/game/MotionMaster.h | 92 + src/game/MovementGenerator.cpp | 23 + src/game/MovementGenerator.h | 98 + src/game/MovementGeneratorImpl.h | 31 + src/game/MovementHandler.cpp | 585 + src/game/NPCHandler.cpp | 832 + src/game/NPCHandler.h | 77 + src/game/NullCreatureAI.cpp | 23 + src/game/NullCreatureAI.h | 42 + src/game/Object.cpp | 1631 ++ src/game/Object.h | 460 + src/game/ObjectAccessor.cpp | 648 + src/game/ObjectAccessor.h | 226 + src/game/ObjectDefines.h | 114 + src/game/ObjectGridLoader.cpp | 304 + src/game/ObjectGridLoader.h | 108 + src/game/ObjectMgr.cpp | 6512 +++++++ src/game/ObjectMgr.h | 780 + src/game/ObjectPosSelector.cpp | 157 + src/game/ObjectPosSelector.h | 155 + src/game/Opcodes.cpp | 1090 ++ src/game/Opcodes.h | 1125 ++ src/game/Path.h | 85 + src/game/Pet.cpp | 1750 ++ src/game/Pet.h | 245 + src/game/PetAI.cpp | 352 + src/game/PetAI.h | 56 + src/game/PetHandler.cpp | 648 + src/game/PetitionsHandler.cpp | 936 + src/game/Player.cpp | 18130 +++++++++++++++++++ src/game/Player.h | 2310 +++ src/game/PlayerDump.cpp | 605 + src/game/PlayerDump.h | 108 + src/game/PointMovementGenerator.cpp | 73 + src/game/PointMovementGenerator.h | 51 + src/game/QueryHandler.cpp | 430 + src/game/QuestDef.cpp | 199 + src/game/QuestDef.h | 330 + src/game/QuestHandler.cpp | 651 + src/game/RandomMovementGenerator.cpp | 159 + src/game/RandomMovementGenerator.h | 50 + src/game/ReactorAI.cpp | 127 + src/game/ReactorAI.h | 44 + src/game/ScriptCalls.cpp | 95 + src/game/ScriptCalls.h | 90 + src/game/SharedDefines.h | 1997 ++ src/game/SkillDiscovery.cpp | 165 + src/game/SkillDiscovery.h | 28 + src/game/SkillExtraItems.cpp | 144 + src/game/SkillExtraItems.h | 30 + src/game/SkillHandler.cpp | 179 + src/game/SocialMgr.cpp | 309 + src/game/SocialMgr.h | 153 + src/game/Spell.cpp | 5115 ++++++ src/game/Spell.h | 694 + src/game/SpellAuraDefines.h | 313 + src/game/SpellAuras.cpp | 6360 +++++++ src/game/SpellAuras.h | 375 + src/game/SpellEffects.cpp | 6090 +++++++ src/game/SpellHandler.cpp | 460 + src/game/SpellMgr.cpp | 2268 +++ src/game/SpellMgr.h | 859 + src/game/StatSystem.cpp | 955 + src/game/TargetedMovementGenerator.cpp | 202 + src/game/TargetedMovementGenerator.h | 73 + src/game/TaxiHandler.cpp | 278 + src/game/TemporarySummon.cpp | 182 + src/game/TemporarySummon.h | 41 + src/game/ThreatManager.cpp | 472 + src/game/ThreatManager.h | 214 + src/game/Tools.h | 26 + src/game/Totem.cpp | 161 + src/game/Totem.h | 60 + src/game/TotemAI.cpp | 123 + src/game/TotemAI.h | 46 + src/game/TradeHandler.cpp | 633 + src/game/Transports.cpp | 525 + src/game/Transports.h | 115 + src/game/Traveller.h | 102 + src/game/Unit.cpp | 10808 +++++++++++ src/game/Unit.h | 1331 ++ src/game/UnitEvents.h | 136 + src/game/UpdateData.cpp | 160 + src/game/UpdateData.h | 67 + src/game/UpdateFields.h | 451 + src/game/UpdateMask.h | 124 + src/game/VoiceChatHandler.cpp | 45 + src/game/WaypointManager.cpp | 272 + src/game/WaypointManager.h | 89 + src/game/WaypointMovementGenerator.cpp | 648 + src/game/WaypointMovementGenerator.h | 136 + src/game/Weather.cpp | 318 + src/game/Weather.h | 72 + src/game/World.cpp | 2460 +++ src/game/World.h | 534 + src/game/WorldLog.cpp | 51 + src/game/WorldLog.h | 76 + src/game/WorldSession.cpp | 463 + src/game/WorldSession.h | 638 + src/game/WorldSocket.cpp | 664 + src/game/WorldSocket.h | 179 + src/game/WorldSocketMgr.cpp | 55 + src/game/WorldSocketMgr.h | 47 + src/game/debugcmds.cpp | 514 + src/game/tools.cpp | 114 + src/shared/Auth/AuthCrypt.cpp | 81 + src/shared/Auth/AuthCrypt.h | 52 + src/shared/Auth/BigNumber.cpp | 207 + src/shared/Auth/BigNumber.h | 94 + src/shared/Auth/Hmac.cpp | 56 + src/shared/Auth/Hmac.h | 46 + src/shared/Auth/Makefile.am | 39 + src/shared/Auth/Sha1.cpp | 65 + src/shared/Auth/Sha1.h | 51 + src/shared/Auth/md5.c | 385 + src/shared/Auth/md5.h | 91 + src/shared/Base.cpp | 67 + src/shared/Base.h | 54 + src/shared/ByteBuffer.h | 479 + src/shared/Common.cpp | 40 + src/shared/Common.h | 171 + src/shared/Config/Config.cpp | 174 + src/shared/Config/Config.h | 58 + src/shared/Config/ConfigEnv.h | 28 + src/shared/Config/ConfigLibrary.vcproj | 137 + src/shared/Config/Makefile.am | 40 + src/shared/Config/dotconfpp/dotconfpp.cpp | 583 + src/shared/Config/dotconfpp/dotconfpp.h | 110 + src/shared/Config/dotconfpp/mempool.cpp | 100 + src/shared/Config/dotconfpp/mempool.h | 46 + src/shared/Database/DBCStores.cpp | 642 + src/shared/Database/DBCStores.h | 202 + src/shared/Database/DBCStructure.h | 937 + src/shared/Database/DBCfmt.cpp | 78 + src/shared/Database/Database.cpp | 171 + src/shared/Database/Database.h | 113 + src/shared/Database/DatabaseEnv.h | 52 + src/shared/Database/DatabaseImpl.h | 146 + src/shared/Database/DatabaseMysql.cpp | 408 + src/shared/Database/DatabaseMysql.h | 77 + src/shared/Database/DatabasePostgre.cpp | 345 + src/shared/Database/DatabasePostgre.h | 75 + src/shared/Database/DatabaseSqlite.cpp | 101 + src/shared/Database/DatabaseSqlite.h | 43 + src/shared/Database/Field.cpp | 65 + src/shared/Database/Field.h | 75 + src/shared/Database/Makefile.am | 62 + src/shared/Database/MySQLDelayThread.h | 30 + src/shared/Database/PGSQLDelayThread.h | 30 + src/shared/Database/QueryResult.h | 64 + src/shared/Database/QueryResultMysql.cpp | 110 + src/shared/Database/QueryResultMysql.h | 48 + src/shared/Database/QueryResultPostgre.cpp | 139 + src/shared/Database/QueryResultPostgre.h | 48 + src/shared/Database/QueryResultSqlite.cpp | 96 + src/shared/Database/QueryResultSqlite.h | 43 + src/shared/Database/SQLStorage.cpp | 191 + src/shared/Database/SQLStorage.h | 68 + src/shared/Database/SqlDelayThread.cpp | 55 + src/shared/Database/SqlDelayThread.h | 48 + src/shared/Database/SqlOperations.cpp | 197 + src/shared/Database/SqlOperations.h | 121 + src/shared/Database/dbcfile.cpp | 243 + src/shared/Database/dbcfile.h | 107 + src/shared/Errors.h | 29 + src/shared/Log.cpp | 762 + src/shared/Log.h | 154 + src/shared/Makefile.am | 117 + src/shared/Mthread.cpp | 205 + src/shared/Mthread.h | 62 + src/shared/PacketLog.cpp | 157 + src/shared/PacketLog.h | 46 + src/shared/ProgressBar.cpp | 80 + src/shared/ProgressBar.h | 40 + src/shared/ServiceWin32.cpp | 260 + src/shared/ServiceWin32.h | 28 + src/shared/SystemConfig.h | 33 + src/shared/Timer.h | 96 + src/shared/Util.cpp | 425 + src/shared/Util.h | 306 + src/shared/WheatyExceptionReport.cpp | 964 + src/shared/WheatyExceptionReport.h | 117 + src/shared/WorldPacket.h | 51 + src/shared/vmap/AABSPTree.h | 1620 ++ src/shared/vmap/BaseModel.cpp | 95 + src/shared/vmap/BaseModel.h | 99 + src/shared/vmap/CoordModelMapping.cpp | 187 + src/shared/vmap/CoordModelMapping.h | 144 + src/shared/vmap/DebugCmdLogger.cpp | 125 + src/shared/vmap/DebugCmdLogger.h | 116 + src/shared/vmap/IVMapManager.h | 99 + src/shared/vmap/Makefile.am | 54 + src/shared/vmap/ManagedModelContainer.cpp | 35 + src/shared/vmap/ManagedModelContainer.h | 49 + src/shared/vmap/ModelContainer.cpp | 375 + src/shared/vmap/ModelContainer.h | 108 + src/shared/vmap/NodeValueAccess.h | 48 + src/shared/vmap/ShortBox.h | 148 + src/shared/vmap/ShortVector.h | 134 + src/shared/vmap/SubModel.cpp | 248 + src/shared/vmap/SubModel.h | 102 + src/shared/vmap/TileAssembler.cpp | 571 + src/shared/vmap/TileAssembler.h | 93 + src/shared/vmap/TreeNode.cpp | 37 + src/shared/vmap/TreeNode.h | 223 + src/shared/vmap/VMapDefinitions.h | 37 + src/shared/vmap/VMapFactory.cpp | 104 + src/shared/vmap/VMapFactory.h | 43 + src/shared/vmap/VMapManager.cpp | 780 + src/shared/vmap/VMapManager.h | 173 + src/shared/vmap/VMapTools.h | 150 + src/trinitycore/CliRunnable.cpp | 1265 ++ src/trinitycore/CliRunnable.h | 33 + src/trinitycore/Main.cpp | 164 + src/trinitycore/Makefile.am | 78 + src/trinitycore/Master.cpp | 492 + src/trinitycore/Master.h | 50 + src/trinitycore/RASocket.cpp | 251 + src/trinitycore/RASocket.h | 65 + src/trinitycore/TrinityCore.ico | Bin 0 -> 34494 bytes src/trinitycore/TrinityCore.rc | 85 + src/trinitycore/WorldRunnable.cpp | 82 + src/trinitycore/WorldRunnable.h | 33 + src/trinitycore/monitor-mangosd | 18 + src/trinitycore/resource.h | 15 + src/trinitycore/run-mangosd | 14 + src/trinitycore/trinitycore.conf.dist | 1027 ++ src/trinityrealm/AuthCodes.h | 73 + src/trinityrealm/AuthSocket.cpp | 975 + src/trinityrealm/AuthSocket.h | 75 + src/trinityrealm/Main.cpp | 344 + src/trinityrealm/Makefile.am | 69 + src/trinityrealm/RealmList.cpp | 100 + src/trinityrealm/RealmList.h | 66 + src/trinityrealm/TrinityRealm.ico | Bin 0 -> 34494 bytes src/trinityrealm/TrinityRealm.rc | 85 + src/trinityrealm/resource.h | 15 + src/trinityrealm/trinityrealm.conf.dist | 116 + 991 files changed, 280377 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/bindings/Makefile.am create mode 100644 src/bindings/interface/Makefile.am create mode 100644 src/bindings/interface/Readme.txt create mode 100644 src/bindings/interface/ScriptMgr.cpp create mode 100644 src/bindings/interface/ScriptMgr.h create mode 100644 src/bindings/interface/Scripts/sc_default.cpp create mode 100644 src/bindings/interface/Scripts/sc_defines.cpp create mode 100644 src/bindings/interface/Scripts/sc_defines.h create mode 100644 src/bindings/interface/config.h create mode 100644 src/bindings/interface/system.cpp create mode 100644 src/bindings/scripts/How to install.txt create mode 100644 src/bindings/scripts/LICENSE.txt create mode 100644 src/bindings/scripts/Makefile.am create mode 100644 src/bindings/scripts/ScriptMgr.cpp create mode 100644 src/bindings/scripts/ScriptMgr.h create mode 100644 src/bindings/scripts/VC71/71ScriptDev2.vcproj create mode 100644 src/bindings/scripts/VC80/80ScriptDev2.vcproj create mode 100644 src/bindings/scripts/VC90/90ScriptDev2.vcproj create mode 100644 src/bindings/scripts/VCProjToLinuxMake.exe create mode 100644 src/bindings/scripts/docs/EventAI.txt create mode 100644 src/bindings/scripts/docs/How to install.txt create mode 100644 src/bindings/scripts/docs/LICENSE.txt create mode 100644 src/bindings/scripts/docs/Script Layout.txt create mode 100644 src/bindings/scripts/docs/ToDo.txt create mode 100644 src/bindings/scripts/include/precompiled.cpp create mode 100644 src/bindings/scripts/include/precompiled.h create mode 100644 src/bindings/scripts/include/sc_creature.cpp create mode 100644 src/bindings/scripts/include/sc_creature.h create mode 100644 src/bindings/scripts/include/sc_gossip.h create mode 100644 src/bindings/scripts/include/sc_grid_searchers.h create mode 100644 src/bindings/scripts/include/sc_instance.h create mode 100644 src/bindings/scripts/patches/MaNGOS-r4106-ScriptDev2.patch create mode 100644 src/bindings/scripts/patches/MaNGOS-r4203-ScriptDev2.patch create mode 100644 src/bindings/scripts/patches/MaNGOS-r4241-ScriptDev2.patch create mode 100644 src/bindings/scripts/patches/MaNGOS-r4798-ScriptDev2.patch create mode 100644 src/bindings/scripts/patches/MaNGOS-r5049-Scriptdev2.patch create mode 100644 src/bindings/scripts/patches/MaNGOS-r6491-Scriptdev2.patch create mode 100644 src/bindings/scripts/patches/MaNGOS-r6657-Scriptdev2.patch create mode 100644 src/bindings/scripts/scripts/areatrigger/areatrigger_scripts.cpp create mode 100644 src/bindings/scripts/scripts/boss/boss_emeriss.cpp create mode 100644 src/bindings/scripts/scripts/boss/boss_lethon.cpp create mode 100644 src/bindings/scripts/scripts/boss/boss_taerar.cpp create mode 100644 src/bindings/scripts/scripts/boss/boss_ysondre.cpp create mode 100644 src/bindings/scripts/scripts/creature/mob_event_ai.cpp create mode 100644 src/bindings/scripts/scripts/creature/mob_event_ai.h create mode 100644 src/bindings/scripts/scripts/creature/mob_generic_creature.cpp create mode 100644 src/bindings/scripts/scripts/creature/simple_ai.cpp create mode 100644 src/bindings/scripts/scripts/creature/simple_ai.h create mode 100644 src/bindings/scripts/scripts/custom/custom_example.cpp create mode 100644 src/bindings/scripts/scripts/custom/custom_gossip_codebox.cpp create mode 100644 src/bindings/scripts/scripts/custom/test.cpp create mode 100644 src/bindings/scripts/scripts/go/go_scripts.cpp create mode 100644 src/bindings/scripts/scripts/guard/guard_ai.cpp create mode 100644 src/bindings/scripts/scripts/guard/guard_ai.h create mode 100644 src/bindings/scripts/scripts/guard/guards.cpp create mode 100644 src/bindings/scripts/scripts/item/item_scripts.cpp create mode 100644 src/bindings/scripts/scripts/item/item_test.cpp create mode 100644 src/bindings/scripts/scripts/npc/npc_escortAI.cpp create mode 100644 src/bindings/scripts/scripts/npc/npc_escortAI.h create mode 100644 src/bindings/scripts/scripts/npc/npc_innkeeper.cpp create mode 100644 src/bindings/scripts/scripts/npc/npc_professions.cpp create mode 100644 src/bindings/scripts/scripts/npc/npcs_special.cpp create mode 100644 src/bindings/scripts/scripts/zone/alterac_mountains/alterac_mountains.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/auchenai_crypts/boss_exarch_maladaar.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_nexusprince_shaffar.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_pandemonius.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_darkweaver_syth.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_tailonking_ikiss.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/def_sethekk_halls.h create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/instance_sethekk_halls.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_murmur.cpp create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/def_shadow_labyrinth.h create mode 100644 src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp create mode 100644 src/bindings/scripts/scripts/zone/azshara/azshara.cpp create mode 100644 src/bindings/scripts/scripts/zone/azshara/boss_azuregos.cpp create mode 100644 src/bindings/scripts/scripts/zone/azuremyst_isle/azuremyst_isle.cpp create mode 100644 src/bindings/scripts/scripts/zone/barrens/the_barrens.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/black_temple.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/boss_bloodboil.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/boss_illidan.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/boss_mother_shahraz.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/boss_reliquary_of_souls.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/boss_shade_of_akama.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/boss_supremus.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/boss_teron_gorefiend.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/boss_warlord_najentus.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/def_black_temple.h create mode 100644 src/bindings/scripts/scripts/zone/black_temple/illidari_council.cpp create mode 100644 src/bindings/scripts/scripts/zone/black_temple/instance_black_temple.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/blackrock_depths.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_ambassador_flamelash.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_angerrel.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_anubshiah.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_doomrel.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_doperel.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_emperor_dagran_thaurissan.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_general_angerforge.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_gloomrel.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_gorosh_the_dervish.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_grizzle.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_haterel.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_high_interrogator_gerstahn.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_magmus.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_moira_bronzebeard.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_seethrel.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_depths/boss_vilerel.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_drakkisath.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_gyth.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_halycon.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_highlord_omokk.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_mother_smolderweb.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_overlord_wyrmthalak.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_pyroguard_emberseer.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_quartermaster_zigris.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_rend_blackhand.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_shadow_hunter_voshgajin.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_the_beast.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackrock_spire/boss_warmaster_voone.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackwing_lair/boss_broodlord_lashlayer.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackwing_lair/boss_chromaggus.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackwing_lair/boss_ebonroc.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackwing_lair/boss_firemaw.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackwing_lair/boss_flamegor.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackwing_lair/boss_nefarian.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackwing_lair/boss_razorgore.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackwing_lair/boss_vaelastrasz.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackwing_lair/boss_victor_nefarius.cpp create mode 100644 src/bindings/scripts/scripts/zone/blackwing_lair/instance_blackwing_lair.cpp create mode 100644 src/bindings/scripts/scripts/zone/blades_edge_mountains/blades_edge_mountains.cpp create mode 100644 src/bindings/scripts/scripts/zone/blasted_lands/blasted_lands.cpp create mode 100644 src/bindings/scripts/scripts/zone/blasted_lands/boss_kruul.cpp create mode 100644 src/bindings/scripts/scripts/zone/bloodmyst_isle/bloodmyst_isle.cpp create mode 100644 src/bindings/scripts/scripts/zone/burning_steppes/burning_steppes.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_aeonus.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_temporus.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_archimonde.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/def_hyjal.h create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjal.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjalAI.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjalAI.h create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/instance_hyjal.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/def_old_hillsbrad.h create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp create mode 100644 src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/def_serpent_shrine.h create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/slave_pens/boss_rokmar.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/def_steam_vault.h create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/instance_steam_vault.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/underbog/boss_ghazan.cpp create mode 100644 src/bindings/scripts/scripts/zone/coilfang_resevoir/underbog/boss_hungarfen.cpp create mode 100644 src/bindings/scripts/scripts/zone/darkshore/darkshore.cpp create mode 100644 src/bindings/scripts/scripts/zone/deadmines/deadmines.cpp create mode 100644 src/bindings/scripts/scripts/zone/deadmines/instance_deadmines.cpp create mode 100644 src/bindings/scripts/scripts/zone/dun_morogh/dun_morogh.cpp create mode 100644 src/bindings/scripts/scripts/zone/dustwallow_marsh/dustwallow_marsh.cpp create mode 100644 src/bindings/scripts/scripts/zone/eastern_plaguelands/eastern_plaguelands.cpp create mode 100644 src/bindings/scripts/scripts/zone/elwynn_forest/elwynn_forest.cpp create mode 100644 src/bindings/scripts/scripts/zone/eversong_woods/eversong_woods.cpp create mode 100644 src/bindings/scripts/scripts/zone/felwood/felwood.cpp create mode 100644 src/bindings/scripts/scripts/zone/feralas/feralas.cpp create mode 100644 src/bindings/scripts/scripts/zone/ghostlands/ghostlands.cpp create mode 100644 src/bindings/scripts/scripts/zone/gruuls_lair/boss_gruul.cpp create mode 100644 src/bindings/scripts/scripts/zone/gruuls_lair/boss_high_king_maulgar.cpp create mode 100644 src/bindings/scripts/scripts/zone/gruuls_lair/def_gruuls_lair.h create mode 100644 src/bindings/scripts/scripts/zone/gruuls_lair/instance_gruuls_lair.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_broggok.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_the_maker.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/def_magtheridons_lair.h create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_nethekurse.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/def_shattered_halls.h create mode 100644 src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_peninsula/boss_doomlord_kazzak.cpp create mode 100644 src/bindings/scripts/scripts/zone/hellfire_peninsula/hellfire_peninsula.cpp create mode 100644 src/bindings/scripts/scripts/zone/ironforge/ironforge.cpp create mode 100644 src/bindings/scripts/scripts/zone/isle_of_queldanas/isle_of_queldanas.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/boss_curator.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/boss_maiden_of_virtue.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/boss_midnight.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/boss_moroes.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/boss_netherspite.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/boss_nightbane.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/boss_shade_of_aran.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/boss_terestian_illhoof.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/bosses_opera.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/def_karazhan.h create mode 100644 src/bindings/scripts/scripts/zone/karazhan/instance_karazhan.cpp create mode 100644 src/bindings/scripts/scripts/zone/karazhan/karazhan.cpp create mode 100644 src/bindings/scripts/scripts/zone/loch_modan/loch_modan.cpp create mode 100644 src/bindings/scripts/scripts/zone/magisters_terrace/boss_felblood_kaelthas.cpp create mode 100644 src/bindings/scripts/scripts/zone/magisters_terrace/boss_priestess_delrissa.cpp create mode 100644 src/bindings/scripts/scripts/zone/magisters_terrace/boss_selin_fireheart.cpp create mode 100644 src/bindings/scripts/scripts/zone/magisters_terrace/boss_vexallus.cpp create mode 100644 src/bindings/scripts/scripts/zone/magisters_terrace/def_magisters_terrace.h create mode 100644 src/bindings/scripts/scripts/zone/magisters_terrace/instance_magisters_terrace.cpp create mode 100644 src/bindings/scripts/scripts/zone/maraudon/boss_celebras_the_cursed.cpp create mode 100644 src/bindings/scripts/scripts/zone/maraudon/boss_landslide.cpp create mode 100644 src/bindings/scripts/scripts/zone/maraudon/boss_noxxion.cpp create mode 100644 src/bindings/scripts/scripts/zone/maraudon/boss_princess_theradras.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/boss_baron_geddon.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/boss_garr.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/boss_gehennas.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/boss_golemagg.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/boss_lucifron.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/boss_magmadar.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/boss_majordomo_executus.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/boss_ragnaros.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/boss_shazzrah.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/boss_sulfuron_harbinger.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/def_molten_core.h create mode 100644 src/bindings/scripts/scripts/zone/molten_core/instance_molten_core.cpp create mode 100644 src/bindings/scripts/scripts/zone/molten_core/molten_core.cpp create mode 100644 src/bindings/scripts/scripts/zone/moonglade/moonglade.cpp create mode 100644 src/bindings/scripts/scripts/zone/mulgore/mulgore.cpp create mode 100644 src/bindings/scripts/scripts/zone/nagrand/nagrand.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_anubrekhan.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_faerlina.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_feugen.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_gluth.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_gothik.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_grobbulus.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_heigan.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_highlord_mograine.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_kelthuzad.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_lady_blaumeux.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_loatheb.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_maexxna.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_noth.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_patchwerk.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_razuvious.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_sapphiron.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_sir_zeliek.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_stalagg.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_thaddius.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/boss_thane_korthazz.cpp create mode 100644 src/bindings/scripts/scripts/zone/naxxramas/instance_naxxramas.cpp create mode 100644 src/bindings/scripts/scripts/zone/netherstorm/netherstorm.cpp create mode 100644 src/bindings/scripts/scripts/zone/onyxias_lair/boss_onyxia.cpp create mode 100644 src/bindings/scripts/scripts/zone/orgrimmar/orgrimmar.cpp create mode 100644 src/bindings/scripts/scripts/zone/razorfen_downs/boss_amnennar_the_coldbringer.cpp create mode 100644 src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_ayamiss.cpp create mode 100644 src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_buru.cpp create mode 100644 src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_kurinnaxx.cpp create mode 100644 src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_moam.cpp create mode 100644 src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_ossirian.cpp create mode 100644 src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_rajaxx.cpp create mode 100644 src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp create mode 100644 src/bindings/scripts/scripts/zone/scarlet_monastery/boss_arcanist_doan.cpp create mode 100644 src/bindings/scripts/scripts/zone/scarlet_monastery/boss_azshir_the_sleepless.cpp create mode 100644 src/bindings/scripts/scripts/zone/scarlet_monastery/boss_bloodmage_thalnos.cpp create mode 100644 src/bindings/scripts/scripts/zone/scarlet_monastery/boss_herod.cpp create mode 100644 src/bindings/scripts/scripts/zone/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp create mode 100644 src/bindings/scripts/scripts/zone/scarlet_monastery/boss_high_inquisitor_whitemane.cpp create mode 100644 src/bindings/scripts/scripts/zone/scarlet_monastery/boss_houndmaster_loksey.cpp create mode 100644 src/bindings/scripts/scripts/zone/scarlet_monastery/boss_interrogator_vishas.cpp create mode 100644 src/bindings/scripts/scripts/zone/scarlet_monastery/boss_scarlet_commander_mograine.cpp create mode 100644 src/bindings/scripts/scripts/zone/scarlet_monastery/boss_scorn.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_darkmaster_gandling.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_death_knight_darkreaver.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_doctor_theolen_krastinov.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_illucia_barov.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_instructor_malicia.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_jandice_barov.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_kormok.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_lord_alexei_barov.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_lorekeeper_polkelt.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_ras_frostwhisper.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_the_ravenian.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/boss_vectus.cpp create mode 100644 src/bindings/scripts/scripts/zone/scholomance/def_scholomance.h create mode 100644 src/bindings/scripts/scripts/zone/scholomance/instance_scholomance.cpp create mode 100644 src/bindings/scripts/scripts/zone/searing_gorge/searing_gorge.cpp create mode 100644 src/bindings/scripts/scripts/zone/shadowfang_keep/def_shadowfang_keep.h create mode 100644 src/bindings/scripts/scripts/zone/shadowfang_keep/instance_shadowfang_keep.cpp create mode 100644 src/bindings/scripts/scripts/zone/shadowfang_keep/shadowfang_keep.cpp create mode 100644 src/bindings/scripts/scripts/zone/shadowmoon_valley/boss_doomwalker.cpp create mode 100644 src/bindings/scripts/scripts/zone/shadowmoon_valley/shadowmoon_valley.cpp create mode 100644 src/bindings/scripts/scripts/zone/shattrath/shattrath_city.cpp create mode 100644 src/bindings/scripts/scripts/zone/silithus/silithus.cpp create mode 100644 src/bindings/scripts/scripts/zone/silvermoon/silvermoon_city.cpp create mode 100644 src/bindings/scripts/scripts/zone/silverpine_forest/silverpine_forest.cpp create mode 100644 src/bindings/scripts/scripts/zone/stonetalon_mountains/stonetalon_mountains.cpp create mode 100644 src/bindings/scripts/scripts/zone/stormwind/stormwind_city.cpp create mode 100644 src/bindings/scripts/scripts/zone/stranglethorn_vale/stranglethorn_vale.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_baron_rivendare.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_baroness_anastari.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_cannon_master_willey.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_dathrohan_balnazzar.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_magistrate_barthilas.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_maleki_the_pallid.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_nerubenkan.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_order_of_silver_hand.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_postmaster_malown.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_ramstein_the_gorger.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/boss_timmy_the_cruel.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/def_stratholme.h create mode 100644 src/bindings/scripts/scripts/zone/stratholme/instance_stratholme.cpp create mode 100644 src/bindings/scripts/scripts/zone/stratholme/stratholme.cpp create mode 100644 src/bindings/scripts/scripts/zone/sunwell_plateau/boss_brutallus.cpp create mode 100644 src/bindings/scripts/scripts/zone/sunwell_plateau/boss_kalecgos.cpp create mode 100644 src/bindings/scripts/scripts/zone/sunwell_plateau/def_sunwell_plateau.h create mode 100644 src/bindings/scripts/scripts/zone/sunwell_plateau/instance_sunwell_plateau.cpp create mode 100644 src/bindings/scripts/scripts/zone/tanaris/tanaris.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/arcatraz.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/def_arcatraz.h create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/instance_arcatraz.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_high_botanist_freywinn.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_laj.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_warp_splinter.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_astromancer.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_kaelthas.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_void_reaver.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/the_eye/def_the_eye.h create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/the_eye/instance_the_eye.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/the_eye/the_eye.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp create mode 100644 src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_bug_trio.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_cthun.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_fankriss.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_huhuran.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_ouro.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_sartura.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_skeram.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_viscidus.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/def_temple_of_ahnqiraj.h create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp create mode 100644 src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp create mode 100644 src/bindings/scripts/scripts/zone/terokkar_forest/terokkar_forest.cpp create mode 100644 src/bindings/scripts/scripts/zone/thunder_bluff/thunder_bluff.cpp create mode 100644 src/bindings/scripts/scripts/zone/tirisfal_glades/tirisfal_glades.cpp create mode 100644 src/bindings/scripts/scripts/zone/uldaman/boss_ironaya.cpp create mode 100644 src/bindings/scripts/scripts/zone/uldaman/uldaman.cpp create mode 100644 src/bindings/scripts/scripts/zone/undercity/undercity.cpp create mode 100644 src/bindings/scripts/scripts/zone/wailing_caverns/instance_wailing_caverns.cpp create mode 100644 src/bindings/scripts/scripts/zone/western_plaguelands/western_plaguelands.cpp create mode 100644 src/bindings/scripts/scripts/zone/winterspring/winterspring.cpp create mode 100644 src/bindings/scripts/scripts/zone/zangarmarsh/zangarmarsh.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulaman/boss_janalai.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulaman/boss_nalorakk.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulaman/def_zulaman.h create mode 100644 src/bindings/scripts/scripts/zone/zulaman/instance_zulaman.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulaman/zulaman.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulfarrak/zulfarrak.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_arlokk.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_gahzranka.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_grilek.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_hakkar.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_hazzarah.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_jeklik.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_jindo.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_mandokir.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_marli.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_renataki.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_thekal.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_venoxis.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/boss_wushoolay.cpp create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/def_zulgurub.h create mode 100644 src/bindings/scripts/scripts/zone/zulgurub/instance_zulgurub.cpp create mode 100644 src/bindings/scripts/sql/Makefile.am create mode 100644 src/bindings/scripts/sql/Makefile.in create mode 100644 src/bindings/scripts/sql/Updates/09_BraveWindfeather.sql create mode 100644 src/bindings/scripts/sql/Updates/11_SilvaFilnaveth.sql create mode 100644 src/bindings/scripts/sql/Updates/27_Vaelastraz.sql create mode 100644 src/bindings/scripts/sql/Updates/Makefile.am create mode 100644 src/bindings/scripts/sql/Updates/Makefile.in create mode 100644 src/bindings/scripts/sql/Updates/r104.sql create mode 100644 src/bindings/scripts/sql/Updates/r108.sql create mode 100644 src/bindings/scripts/sql/Updates/r110.sql create mode 100644 src/bindings/scripts/sql/Updates/r121.sql create mode 100644 src/bindings/scripts/sql/Updates/r123.sql create mode 100644 src/bindings/scripts/sql/Updates/r124.sql create mode 100644 src/bindings/scripts/sql/Updates/r125.sql create mode 100644 src/bindings/scripts/sql/Updates/r128.sql create mode 100644 src/bindings/scripts/sql/Updates/r131.sql create mode 100644 src/bindings/scripts/sql/Updates/r134.sql create mode 100644 src/bindings/scripts/sql/Updates/r136.sql create mode 100644 src/bindings/scripts/sql/Updates/r139.sql create mode 100644 src/bindings/scripts/sql/Updates/r142.sql create mode 100644 src/bindings/scripts/sql/Updates/r144.sql create mode 100644 src/bindings/scripts/sql/Updates/r145.sql create mode 100644 src/bindings/scripts/sql/Updates/r149.sql create mode 100644 src/bindings/scripts/sql/Updates/r150.sql create mode 100644 src/bindings/scripts/sql/Updates/r152.sql create mode 100644 src/bindings/scripts/sql/Updates/r153.sql create mode 100644 src/bindings/scripts/sql/Updates/r157.sql create mode 100644 src/bindings/scripts/sql/Updates/r161.sql create mode 100644 src/bindings/scripts/sql/Updates/r163.sql create mode 100644 src/bindings/scripts/sql/Updates/r164.sql create mode 100644 src/bindings/scripts/sql/Updates/r165.sql create mode 100644 src/bindings/scripts/sql/Updates/r169.sql create mode 100644 src/bindings/scripts/sql/Updates/r170.sql create mode 100644 src/bindings/scripts/sql/Updates/r171.sql create mode 100644 src/bindings/scripts/sql/Updates/r172.sql create mode 100644 src/bindings/scripts/sql/Updates/r174.sql create mode 100644 src/bindings/scripts/sql/Updates/r176.sql create mode 100644 src/bindings/scripts/sql/Updates/r177.sql create mode 100644 src/bindings/scripts/sql/Updates/r178.sql create mode 100644 src/bindings/scripts/sql/Updates/r181.sql create mode 100644 src/bindings/scripts/sql/Updates/r182.sql create mode 100644 src/bindings/scripts/sql/Updates/r183.sql create mode 100644 src/bindings/scripts/sql/Updates/r184.sql create mode 100644 src/bindings/scripts/sql/Updates/r186.sql create mode 100644 src/bindings/scripts/sql/Updates/r187.sql create mode 100644 src/bindings/scripts/sql/Updates/r189.sql create mode 100644 src/bindings/scripts/sql/Updates/r191.sql create mode 100644 src/bindings/scripts/sql/Updates/r192.sql create mode 100644 src/bindings/scripts/sql/Updates/r197.sql create mode 100644 src/bindings/scripts/sql/Updates/r204.sql create mode 100644 src/bindings/scripts/sql/Updates/r206.sql create mode 100644 src/bindings/scripts/sql/Updates/r211.sql create mode 100644 src/bindings/scripts/sql/Updates/r213.sql create mode 100644 src/bindings/scripts/sql/Updates/r214.sql create mode 100644 src/bindings/scripts/sql/Updates/r215.sql create mode 100644 src/bindings/scripts/sql/Updates/r218.sql create mode 100644 src/bindings/scripts/sql/Updates/r219.sql create mode 100644 src/bindings/scripts/sql/Updates/r221_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r227_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r230_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r234_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r237_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r238_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r239_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r240_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r241_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r242_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r243_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r249_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r250_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r253_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r255_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r256_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r257_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r258_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r260_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r261_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r262_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r263_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r264_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r265_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r269_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r270_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r271_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r272_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r273_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r274_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r275_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r281_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r282_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r286_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r289_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r291_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r295_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r297_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r298_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r299_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r304_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r306_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r307_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r308_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r309_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r311_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r312_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r318_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r324_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r327_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r332_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r333_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r334_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r336_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r352_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r355_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r358_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r364_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r367_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r368_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r369_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r374_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r386_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r417_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r428_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r431_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r444_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r445_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r446_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r448_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r462_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r465_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r467_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r473_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r476_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r477_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r479_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r482_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r484_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r486_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r487_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r494_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r501_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r513_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r514_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r515_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r516_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r517_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r518_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r519_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r520_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r521_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r522_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r526_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r528_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r533_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r538_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r547_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r554_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r555_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r56.sql create mode 100644 src/bindings/scripts/sql/Updates/r575_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r576_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r578_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r584_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r59.sql create mode 100644 src/bindings/scripts/sql/Updates/r590_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r591_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r593_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r594_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r596_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r610_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r615_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r617_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r621_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r628_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r63.sql create mode 100644 src/bindings/scripts/sql/Updates/r632_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r633_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r634_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r636_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r637_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r638_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r639_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r642_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r643_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r646_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r647_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r65.sql create mode 100644 src/bindings/scripts/sql/Updates/r656_scriptdev2.sql create mode 100644 src/bindings/scripts/sql/Updates/r658_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r659_mangos.sql create mode 100644 src/bindings/scripts/sql/Updates/r72.sql create mode 100644 src/bindings/scripts/sql/Updates/r78.sql create mode 100644 src/bindings/scripts/sql/Updates/r81.sql create mode 100644 src/bindings/scripts/sql/Updates/r91.sql create mode 100644 src/bindings/scripts/sql/Updates/r92.sql create mode 100644 src/bindings/scripts/sql/Updates/r97.sql create mode 100644 src/bindings/scripts/sql/create_database.sql create mode 100644 src/bindings/scripts/sql/mangos_full_scripts.sql create mode 100644 src/bindings/scripts/sql/old/mangos_old_spells.sql create mode 100644 src/bindings/scripts/sql/optional/mangos_optional_generic_creature.sql create mode 100644 src/bindings/scripts/sql/scriptdev2_structure.sql create mode 100644 src/bindings/scripts/system.cpp create mode 100644 src/bindings/scripts/trinityscript.conf.dist create mode 100644 src/framework/Dynamic/FactoryHolder.h create mode 100644 src/framework/Dynamic/ObjectRegistry.h create mode 100644 src/framework/GameSystem/Grid.h create mode 100644 src/framework/GameSystem/GridLoader.h create mode 100644 src/framework/GameSystem/GridRefManager.h create mode 100644 src/framework/GameSystem/GridReference.h create mode 100644 src/framework/GameSystem/NGrid.h create mode 100644 src/framework/GameSystem/TypeContainer.h create mode 100644 src/framework/GameSystem/TypeContainerFunctions.h create mode 100644 src/framework/GameSystem/TypeContainerFunctionsPtr.h create mode 100644 src/framework/GameSystem/TypeContainerVisitor.h create mode 100644 src/framework/Makefile.am create mode 100644 src/framework/Network/SocketDefines.h create mode 100644 src/framework/Platform/CompilerDefs.h create mode 100644 src/framework/Platform/Define.h create mode 100644 src/framework/Policies/CreationPolicy.h create mode 100644 src/framework/Policies/ObjectLifeTime.cpp create mode 100644 src/framework/Policies/ObjectLifeTime.h create mode 100644 src/framework/Policies/Singleton.h create mode 100644 src/framework/Policies/SingletonImp.h create mode 100644 src/framework/Policies/ThreadingModel.h create mode 100644 src/framework/Utilities/ByteConverter.h create mode 100644 src/framework/Utilities/Callback.h create mode 100644 src/framework/Utilities/CountedReference/Reference.h create mode 100644 src/framework/Utilities/CountedReference/ReferenceHolder.h create mode 100644 src/framework/Utilities/CountedReference/ReferenceImpl.h create mode 100644 src/framework/Utilities/EventProcessor.cpp create mode 100644 src/framework/Utilities/EventProcessor.h create mode 100644 src/framework/Utilities/HashMap.h create mode 100644 src/framework/Utilities/LinkedList.h create mode 100644 src/framework/Utilities/LinkedReference/RefManager.h create mode 100644 src/framework/Utilities/LinkedReference/Reference.h create mode 100644 src/framework/Utilities/TypeList.h create mode 100644 src/game/AccountMgr.cpp create mode 100644 src/game/AccountMgr.h create mode 100644 src/game/AddonHandler.cpp create mode 100644 src/game/AddonHandler.h create mode 100644 src/game/AggressorAI.cpp create mode 100644 src/game/AggressorAI.h create mode 100644 src/game/AnimalRandomMovementGenerator.h create mode 100644 src/game/ArenaTeam.cpp create mode 100644 src/game/ArenaTeam.h create mode 100644 src/game/ArenaTeamHandler.cpp create mode 100644 src/game/AuctionHouse.cpp create mode 100644 src/game/AuctionHouseObject.h create mode 100644 src/game/Bag.cpp create mode 100644 src/game/Bag.h create mode 100644 src/game/BattleGround.cpp create mode 100644 src/game/BattleGround.h create mode 100644 src/game/BattleGroundAA.cpp create mode 100644 src/game/BattleGroundAA.h create mode 100644 src/game/BattleGroundAB.cpp create mode 100644 src/game/BattleGroundAB.h create mode 100644 src/game/BattleGroundAV.cpp create mode 100644 src/game/BattleGroundAV.h create mode 100644 src/game/BattleGroundBE.cpp create mode 100644 src/game/BattleGroundBE.h create mode 100644 src/game/BattleGroundEY.cpp create mode 100644 src/game/BattleGroundEY.h create mode 100644 src/game/BattleGroundHandler.cpp create mode 100644 src/game/BattleGroundMgr.cpp create mode 100644 src/game/BattleGroundMgr.h create mode 100644 src/game/BattleGroundNA.cpp create mode 100644 src/game/BattleGroundNA.h create mode 100644 src/game/BattleGroundRL.cpp create mode 100644 src/game/BattleGroundRL.h create mode 100644 src/game/BattleGroundWS.cpp create mode 100644 src/game/BattleGroundWS.h create mode 100644 src/game/Cell.h create mode 100644 src/game/CellImpl.h create mode 100644 src/game/Channel.cpp create mode 100644 src/game/Channel.h create mode 100644 src/game/ChannelHandler.cpp create mode 100644 src/game/ChannelMgr.h create mode 100644 src/game/CharacterHandler.cpp create mode 100644 src/game/Chat.cpp create mode 100644 src/game/Chat.h create mode 100644 src/game/ChatHandler.cpp create mode 100644 src/game/CombatHandler.cpp create mode 100644 src/game/ConfusedMovementGenerator.cpp create mode 100644 src/game/ConfusedMovementGenerator.h create mode 100644 src/game/Corpse.cpp create mode 100644 src/game/Corpse.h create mode 100644 src/game/Creature.cpp create mode 100644 src/game/Creature.h create mode 100644 src/game/CreatureAI.cpp create mode 100644 src/game/CreatureAI.h create mode 100644 src/game/CreatureAIImpl.h create mode 100644 src/game/CreatureAIRegistry.cpp create mode 100644 src/game/CreatureAIRegistry.h create mode 100644 src/game/CreatureAISelector.cpp create mode 100644 src/game/CreatureAISelector.h create mode 100644 src/game/DestinationHolder.cpp create mode 100644 src/game/DestinationHolder.h create mode 100644 src/game/DestinationHolderImp.h create mode 100644 src/game/DuelHandler.cpp create mode 100644 src/game/DynamicObject.cpp create mode 100644 src/game/DynamicObject.h create mode 100644 src/game/FleeingMovementGenerator.cpp create mode 100644 src/game/FleeingMovementGenerator.h create mode 100644 src/game/FollowerRefManager.h create mode 100644 src/game/FollowerReference.cpp create mode 100644 src/game/FollowerReference.h create mode 100644 src/game/Formulas.h create mode 100644 src/game/GameEvent.cpp create mode 100644 src/game/GameEvent.h create mode 100644 src/game/GameObject.cpp create mode 100644 src/game/GameObject.h create mode 100644 src/game/GlobalEvents.cpp create mode 100644 src/game/GlobalEvents.h create mode 100644 src/game/GossipDef.cpp create mode 100644 src/game/GossipDef.h create mode 100644 src/game/GridDefines.h create mode 100644 src/game/GridNotifiers.cpp create mode 100644 src/game/GridNotifiers.h create mode 100644 src/game/GridNotifiersImpl.h create mode 100644 src/game/GridStates.cpp create mode 100644 src/game/GridStates.h create mode 100644 src/game/Group.cpp create mode 100644 src/game/Group.h create mode 100644 src/game/GroupHandler.cpp create mode 100644 src/game/GroupRefManager.h create mode 100644 src/game/GroupReference.cpp create mode 100644 src/game/GroupReference.h create mode 100644 src/game/GuardAI.cpp create mode 100644 src/game/GuardAI.h create mode 100644 src/game/Guild.cpp create mode 100644 src/game/Guild.h create mode 100644 src/game/GuildHandler.cpp create mode 100644 src/game/HateMatrix.h create mode 100644 src/game/HomeMovementGenerator.cpp create mode 100644 src/game/HomeMovementGenerator.h create mode 100644 src/game/HostilRefManager.cpp create mode 100644 src/game/HostilRefManager.h create mode 100644 src/game/IdleMovementGenerator.cpp create mode 100644 src/game/IdleMovementGenerator.h create mode 100644 src/game/InstanceData.cpp create mode 100644 src/game/InstanceData.h create mode 100644 src/game/InstanceSaveMgr.cpp create mode 100644 src/game/InstanceSaveMgr.h create mode 100644 src/game/Item.cpp create mode 100644 src/game/Item.h create mode 100644 src/game/ItemEnchantmentMgr.cpp create mode 100644 src/game/ItemEnchantmentMgr.h create mode 100644 src/game/ItemHandler.cpp create mode 100644 src/game/ItemPrototype.h create mode 100644 src/game/LFGHandler.cpp create mode 100644 src/game/Language.h create mode 100644 src/game/Level0.cpp create mode 100644 src/game/Level1.cpp create mode 100644 src/game/Level2.cpp create mode 100644 src/game/Level3.cpp create mode 100644 src/game/LootHandler.cpp create mode 100644 src/game/LootMgr.cpp create mode 100644 src/game/LootMgr.h create mode 100644 src/game/Mail.cpp create mode 100644 src/game/Mail.h create mode 100644 src/game/Makefile.am create mode 100644 src/game/Map.cpp create mode 100644 src/game/Map.h create mode 100644 src/game/MapInstanced.cpp create mode 100644 src/game/MapInstanced.h create mode 100644 src/game/MapManager.cpp create mode 100644 src/game/MapManager.h create mode 100644 src/game/MiscHandler.cpp create mode 100644 src/game/MotionMaster.cpp create mode 100644 src/game/MotionMaster.h create mode 100644 src/game/MovementGenerator.cpp create mode 100644 src/game/MovementGenerator.h create mode 100644 src/game/MovementGeneratorImpl.h create mode 100644 src/game/MovementHandler.cpp create mode 100644 src/game/NPCHandler.cpp create mode 100644 src/game/NPCHandler.h create mode 100644 src/game/NullCreatureAI.cpp create mode 100644 src/game/NullCreatureAI.h create mode 100644 src/game/Object.cpp create mode 100644 src/game/Object.h create mode 100644 src/game/ObjectAccessor.cpp create mode 100644 src/game/ObjectAccessor.h create mode 100644 src/game/ObjectDefines.h create mode 100644 src/game/ObjectGridLoader.cpp create mode 100644 src/game/ObjectGridLoader.h create mode 100644 src/game/ObjectMgr.cpp create mode 100644 src/game/ObjectMgr.h create mode 100644 src/game/ObjectPosSelector.cpp create mode 100644 src/game/ObjectPosSelector.h create mode 100644 src/game/Opcodes.cpp create mode 100644 src/game/Opcodes.h create mode 100644 src/game/Path.h create mode 100644 src/game/Pet.cpp create mode 100644 src/game/Pet.h create mode 100644 src/game/PetAI.cpp create mode 100644 src/game/PetAI.h create mode 100644 src/game/PetHandler.cpp create mode 100644 src/game/PetitionsHandler.cpp create mode 100644 src/game/Player.cpp create mode 100644 src/game/Player.h create mode 100644 src/game/PlayerDump.cpp create mode 100644 src/game/PlayerDump.h create mode 100644 src/game/PointMovementGenerator.cpp create mode 100644 src/game/PointMovementGenerator.h create mode 100644 src/game/QueryHandler.cpp create mode 100644 src/game/QuestDef.cpp create mode 100644 src/game/QuestDef.h create mode 100644 src/game/QuestHandler.cpp create mode 100644 src/game/RandomMovementGenerator.cpp create mode 100644 src/game/RandomMovementGenerator.h create mode 100644 src/game/ReactorAI.cpp create mode 100644 src/game/ReactorAI.h create mode 100644 src/game/ScriptCalls.cpp create mode 100644 src/game/ScriptCalls.h create mode 100644 src/game/SharedDefines.h create mode 100644 src/game/SkillDiscovery.cpp create mode 100644 src/game/SkillDiscovery.h create mode 100644 src/game/SkillExtraItems.cpp create mode 100644 src/game/SkillExtraItems.h create mode 100644 src/game/SkillHandler.cpp create mode 100644 src/game/SocialMgr.cpp create mode 100644 src/game/SocialMgr.h create mode 100644 src/game/Spell.cpp create mode 100644 src/game/Spell.h create mode 100644 src/game/SpellAuraDefines.h create mode 100644 src/game/SpellAuras.cpp create mode 100644 src/game/SpellAuras.h create mode 100644 src/game/SpellEffects.cpp create mode 100644 src/game/SpellHandler.cpp create mode 100644 src/game/SpellMgr.cpp create mode 100644 src/game/SpellMgr.h create mode 100644 src/game/StatSystem.cpp create mode 100644 src/game/TargetedMovementGenerator.cpp create mode 100644 src/game/TargetedMovementGenerator.h create mode 100644 src/game/TaxiHandler.cpp create mode 100644 src/game/TemporarySummon.cpp create mode 100644 src/game/TemporarySummon.h create mode 100644 src/game/ThreatManager.cpp create mode 100644 src/game/ThreatManager.h create mode 100644 src/game/Tools.h create mode 100644 src/game/Totem.cpp create mode 100644 src/game/Totem.h create mode 100644 src/game/TotemAI.cpp create mode 100644 src/game/TotemAI.h create mode 100644 src/game/TradeHandler.cpp create mode 100644 src/game/Transports.cpp create mode 100644 src/game/Transports.h create mode 100644 src/game/Traveller.h create mode 100644 src/game/Unit.cpp create mode 100644 src/game/Unit.h create mode 100644 src/game/UnitEvents.h create mode 100644 src/game/UpdateData.cpp create mode 100644 src/game/UpdateData.h create mode 100644 src/game/UpdateFields.h create mode 100644 src/game/UpdateMask.h create mode 100644 src/game/VoiceChatHandler.cpp create mode 100644 src/game/WaypointManager.cpp create mode 100644 src/game/WaypointManager.h create mode 100644 src/game/WaypointMovementGenerator.cpp create mode 100644 src/game/WaypointMovementGenerator.h create mode 100644 src/game/Weather.cpp create mode 100644 src/game/Weather.h create mode 100644 src/game/World.cpp create mode 100644 src/game/World.h create mode 100644 src/game/WorldLog.cpp create mode 100644 src/game/WorldLog.h create mode 100644 src/game/WorldSession.cpp create mode 100644 src/game/WorldSession.h create mode 100644 src/game/WorldSocket.cpp create mode 100644 src/game/WorldSocket.h create mode 100644 src/game/WorldSocketMgr.cpp create mode 100644 src/game/WorldSocketMgr.h create mode 100644 src/game/debugcmds.cpp create mode 100644 src/game/tools.cpp create mode 100644 src/shared/Auth/AuthCrypt.cpp create mode 100644 src/shared/Auth/AuthCrypt.h create mode 100644 src/shared/Auth/BigNumber.cpp create mode 100644 src/shared/Auth/BigNumber.h create mode 100644 src/shared/Auth/Hmac.cpp create mode 100644 src/shared/Auth/Hmac.h create mode 100644 src/shared/Auth/Makefile.am create mode 100644 src/shared/Auth/Sha1.cpp create mode 100644 src/shared/Auth/Sha1.h create mode 100644 src/shared/Auth/md5.c create mode 100644 src/shared/Auth/md5.h create mode 100644 src/shared/Base.cpp create mode 100644 src/shared/Base.h create mode 100644 src/shared/ByteBuffer.h create mode 100644 src/shared/Common.cpp create mode 100644 src/shared/Common.h create mode 100644 src/shared/Config/Config.cpp create mode 100644 src/shared/Config/Config.h create mode 100644 src/shared/Config/ConfigEnv.h create mode 100644 src/shared/Config/ConfigLibrary.vcproj create mode 100644 src/shared/Config/Makefile.am create mode 100644 src/shared/Config/dotconfpp/dotconfpp.cpp create mode 100644 src/shared/Config/dotconfpp/dotconfpp.h create mode 100644 src/shared/Config/dotconfpp/mempool.cpp create mode 100644 src/shared/Config/dotconfpp/mempool.h create mode 100644 src/shared/Database/DBCStores.cpp create mode 100644 src/shared/Database/DBCStores.h create mode 100644 src/shared/Database/DBCStructure.h create mode 100644 src/shared/Database/DBCfmt.cpp create mode 100644 src/shared/Database/Database.cpp create mode 100644 src/shared/Database/Database.h create mode 100644 src/shared/Database/DatabaseEnv.h create mode 100644 src/shared/Database/DatabaseImpl.h create mode 100644 src/shared/Database/DatabaseMysql.cpp create mode 100644 src/shared/Database/DatabaseMysql.h create mode 100644 src/shared/Database/DatabasePostgre.cpp create mode 100644 src/shared/Database/DatabasePostgre.h create mode 100644 src/shared/Database/DatabaseSqlite.cpp create mode 100644 src/shared/Database/DatabaseSqlite.h create mode 100644 src/shared/Database/Field.cpp create mode 100644 src/shared/Database/Field.h create mode 100644 src/shared/Database/Makefile.am create mode 100644 src/shared/Database/MySQLDelayThread.h create mode 100644 src/shared/Database/PGSQLDelayThread.h create mode 100644 src/shared/Database/QueryResult.h create mode 100644 src/shared/Database/QueryResultMysql.cpp create mode 100644 src/shared/Database/QueryResultMysql.h create mode 100644 src/shared/Database/QueryResultPostgre.cpp create mode 100644 src/shared/Database/QueryResultPostgre.h create mode 100644 src/shared/Database/QueryResultSqlite.cpp create mode 100644 src/shared/Database/QueryResultSqlite.h create mode 100644 src/shared/Database/SQLStorage.cpp create mode 100644 src/shared/Database/SQLStorage.h create mode 100644 src/shared/Database/SqlDelayThread.cpp create mode 100644 src/shared/Database/SqlDelayThread.h create mode 100644 src/shared/Database/SqlOperations.cpp create mode 100644 src/shared/Database/SqlOperations.h create mode 100644 src/shared/Database/dbcfile.cpp create mode 100644 src/shared/Database/dbcfile.h create mode 100644 src/shared/Errors.h create mode 100644 src/shared/Log.cpp create mode 100644 src/shared/Log.h create mode 100644 src/shared/Makefile.am create mode 100644 src/shared/Mthread.cpp create mode 100644 src/shared/Mthread.h create mode 100644 src/shared/PacketLog.cpp create mode 100644 src/shared/PacketLog.h create mode 100644 src/shared/ProgressBar.cpp create mode 100644 src/shared/ProgressBar.h create mode 100644 src/shared/ServiceWin32.cpp create mode 100644 src/shared/ServiceWin32.h create mode 100644 src/shared/SystemConfig.h create mode 100644 src/shared/Timer.h create mode 100644 src/shared/Util.cpp create mode 100644 src/shared/Util.h create mode 100644 src/shared/WheatyExceptionReport.cpp create mode 100644 src/shared/WheatyExceptionReport.h create mode 100644 src/shared/WorldPacket.h create mode 100644 src/shared/vmap/AABSPTree.h create mode 100644 src/shared/vmap/BaseModel.cpp create mode 100644 src/shared/vmap/BaseModel.h create mode 100644 src/shared/vmap/CoordModelMapping.cpp create mode 100644 src/shared/vmap/CoordModelMapping.h create mode 100644 src/shared/vmap/DebugCmdLogger.cpp create mode 100644 src/shared/vmap/DebugCmdLogger.h create mode 100644 src/shared/vmap/IVMapManager.h create mode 100644 src/shared/vmap/Makefile.am create mode 100644 src/shared/vmap/ManagedModelContainer.cpp create mode 100644 src/shared/vmap/ManagedModelContainer.h create mode 100644 src/shared/vmap/ModelContainer.cpp create mode 100644 src/shared/vmap/ModelContainer.h create mode 100644 src/shared/vmap/NodeValueAccess.h create mode 100644 src/shared/vmap/ShortBox.h create mode 100644 src/shared/vmap/ShortVector.h create mode 100644 src/shared/vmap/SubModel.cpp create mode 100644 src/shared/vmap/SubModel.h create mode 100644 src/shared/vmap/TileAssembler.cpp create mode 100644 src/shared/vmap/TileAssembler.h create mode 100644 src/shared/vmap/TreeNode.cpp create mode 100644 src/shared/vmap/TreeNode.h create mode 100644 src/shared/vmap/VMapDefinitions.h create mode 100644 src/shared/vmap/VMapFactory.cpp create mode 100644 src/shared/vmap/VMapFactory.h create mode 100644 src/shared/vmap/VMapManager.cpp create mode 100644 src/shared/vmap/VMapManager.h create mode 100644 src/shared/vmap/VMapTools.h create mode 100644 src/trinitycore/CliRunnable.cpp create mode 100644 src/trinitycore/CliRunnable.h create mode 100644 src/trinitycore/Main.cpp create mode 100644 src/trinitycore/Makefile.am create mode 100644 src/trinitycore/Master.cpp create mode 100644 src/trinitycore/Master.h create mode 100644 src/trinitycore/RASocket.cpp create mode 100644 src/trinitycore/RASocket.h create mode 100644 src/trinitycore/TrinityCore.ico create mode 100644 src/trinitycore/TrinityCore.rc create mode 100644 src/trinitycore/WorldRunnable.cpp create mode 100644 src/trinitycore/WorldRunnable.h create mode 100644 src/trinitycore/monitor-mangosd create mode 100644 src/trinitycore/resource.h create mode 100644 src/trinitycore/run-mangosd create mode 100644 src/trinitycore/trinitycore.conf.dist create mode 100644 src/trinityrealm/AuthCodes.h create mode 100644 src/trinityrealm/AuthSocket.cpp create mode 100644 src/trinityrealm/AuthSocket.h create mode 100644 src/trinityrealm/Main.cpp create mode 100644 src/trinityrealm/Makefile.am create mode 100644 src/trinityrealm/RealmList.cpp create mode 100644 src/trinityrealm/RealmList.h create mode 100644 src/trinityrealm/TrinityRealm.ico create mode 100644 src/trinityrealm/TrinityRealm.rc create mode 100644 src/trinityrealm/resource.h create mode 100644 src/trinityrealm/trinityrealm.conf.dist (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000000..e9dd1c61380 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,23 @@ +# Copyright (C) 2005-2008 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse +SUBDIRS = framework shared trinityrealm game bindings trinitycore + +## Additional files to include when running 'make dist' +# Nothing yet. diff --git a/src/bindings/Makefile.am b/src/bindings/Makefile.am new file mode 100644 index 00000000000..69043c94dd4 --- /dev/null +++ b/src/bindings/Makefile.am @@ -0,0 +1,23 @@ +# Copyright (C) 2005-2008 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# interface folder is disabled for now + +if USE_TSCRIPTS +SUBDIRS = scripts +else +SUBDIRS = interface +endif diff --git a/src/bindings/interface/Makefile.am b/src/bindings/interface/Makefile.am new file mode 100644 index 00000000000..5da807d8a16 --- /dev/null +++ b/src/bindings/interface/Makefile.am @@ -0,0 +1,52 @@ +# Copyright (C) 2005-2008 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## Build MaNGOS script library as shared library. +# libmangosscript shared library will later be reused by world server daemon. +lib_LTLIBRARIES = libtrinityscript.la + +libtrinityscript_la_CPPFLAGS = \ +$(MYSQL_INCLUDES) \ +$(POSTGRE_INCLUDES) \ +-I$(top_srcdir)/dep/include \ +-I$(top_srcdir)/src/shared \ +-I$(top_srcdir)/src/framework \ +-I$(top_srcdir)/src/game + +libtrinityscript_la_SOURCES = \ + ScriptMgr.cpp \ + ScriptMgr.h \ + config.h \ + system.cpp \ + Scripts/sc_default.cpp \ + Scripts/sc_defines.cpp \ + Scripts/sc_defines.h + +## libtool settings +# API versioning +libtrinityscript_la_LIBFLAGS = -version-info 0:0:1 +libtrinityscript_la_LIBADD = $(MYSQL_LIBS) $(POSTGRE_LIBS) + +## Additional files to include when running 'make dist' +# Scripts defaults. +EXTRA_DIST = \ + Scripts/sc_default.cpp \ + Scripts/sc_defines.cpp \ + Scripts/sc_defines.h diff --git a/src/bindings/interface/Readme.txt b/src/bindings/interface/Readme.txt new file mode 100644 index 00000000000..e152162de8e --- /dev/null +++ b/src/bindings/interface/Readme.txt @@ -0,0 +1,32 @@ + +** HOW TO SCRIPT IN C++ ** + +1 - create a file myscript.cpp in scripts folder. +2 - copy the content of script_default.cpp, it as the structure on how the scripting fuctions are organized. + dont forget to change the name of fuctions, like GossipHello_default to GossipHello_myscript. + +3 - in fuction AddSC_default change to AddSC_myscript. +4 - newscript->Name="default"; change the string to "myscript" this name is the one to be called from the db +5 - dont forget to change the name in here to newscript->pGossipHello = &GossipHello_default; this is where the scripted fuctions are stored. +6 - and last thing is in ScriptMgr.cpp + +add your AddSC_myscript in here + +// -- Scripts to be added -- +extern void AddSC_default(); +// ------------------- + +and here + +// -- Inicialize the Scripts to be Added -- + AddSC_default(); + // ---------------------------------------- + +now start using the player fuctions to script ;) +see the sc_defines.h for some fuctions to use. + +hope it helps, any question use our forum. + +copy libscript.so and libscript.a to your server/lib path + +made by: mmcs. diff --git a/src/bindings/interface/ScriptMgr.cpp b/src/bindings/interface/ScriptMgr.cpp new file mode 100644 index 00000000000..100159803f5 --- /dev/null +++ b/src/bindings/interface/ScriptMgr.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "ScriptMgr.h" +#include "../../game/GossipDef.h" +#include "../../game/GameObject.h" +#include "../../game/Player.h" +#include "../../game/Map.h" +#include "../../game/ObjectMgr.h" + +//uint8 loglevel = 0; +int nrscripts; +Script *m_scripts[MAX_SCRIPTS]; +InstanceDataScript* m_instance_scripts[MAX_INSTANCE_SCRIPTS]; +int num_inst_scripts; + +// -- Scripts to be added -- +extern void AddSC_default(); +// ------------------- + +MANGOS_DLL_EXPORT +void ScriptsFree() +{ // Free resources before library unload + for(int i=0;iName == Name ) + return m_scripts[i]; + } + return NULL; +} + +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("DEBUG: 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("DEBUG: Gossip selection, 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 *_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 *_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 *_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 *_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 = NULL; + + 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 *_Quest ) +{ + Script *tmpscript = NULL; + + 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 *_Quest ) +{ + Script *tmpscript = NULL; + + 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 = NULL; + + 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 *_Quest ) +{ + Script *tmpscript = NULL; + + 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 *_Quest, uint32 opt ) +{ + Script *tmpscript = NULL; + + 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 +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 +bool ItemUse( Player *player, Item* _Item, SpellCastTargets const& targets) +{ + Script *tmpscript = NULL; + + tmpscript = GetScriptByName(_Item->GetProto()->ScriptName); + if(!tmpscript || !tmpscript->pItemUse) return false; + + return tmpscript->pItemUse(player,_Item,targets); +} + +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 +InstanceData* CreateInstanceData(Map *map) +{ + if(!map->IsDungeon()) return NULL; + std::string name = ((InstanceMap*)map)->GetScript(); + if(!name.empty()) + for(int i=0;iname == name) + return m_instance_scripts[i]->GetInstanceData(map); + return NULL; +} + +void ScriptedAI::UpdateAI(const uint32) +{ + //Check if we have a current target + if( m_creature->isAlive() && m_creature->SelectHostilTarget() && m_creature->getVictim()) + { + //If we are within range melee the target + if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + { + if( m_creature->isAttackReady() ) + { + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); + } + } + } +} + +void ScriptedAI::EnterEvadeMode() +{ + if( m_creature->isAlive() ) + DoGoHome(); +} + +void ScriptedAI::DoStartAttack(Unit* victim) +{ + if( m_creature->Attack(victim, true) ) + m_creature->GetMotionMaster()->MoveChase(victim); +} + +void ScriptedAI::DoStopAttack() +{ + if( m_creature->getVictim() != NULL ) + { + m_creature->AttackStop(); + } +} + +void ScriptedAI::DoGoHome() +{ + if( !m_creature->getVictim() && m_creature->isAlive() ) + m_creature->GetMotionMaster()->MoveTargetedHome(); +} diff --git a/src/bindings/interface/ScriptMgr.h b/src/bindings/interface/ScriptMgr.h new file mode 100644 index 00000000000..0cc0b4362b8 --- /dev/null +++ b/src/bindings/interface/ScriptMgr.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SCRIPTMGR_H +#define SCRIPTMGR_H + +//Only required includes +#include "../../game/CreatureAI.h" +#include "../../game/Creature.h" +#include "../../game/InstanceData.h" + +class Player; +class Creature; +class Quest; +class Item; +class GameObject; +class SpellCastTargets; +class Map; + +#define MAX_SCRIPTS 1000 +#define MAX_INSTANCE_SCRIPTS 1000 + +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) + {} + + std::string Name; + + // -- Quest/gossip Methods to be scripted -- + bool (*pGossipHello )(Player *player, Creature *_Creature); + bool (*pQuestAccept )(Player *player, Creature *_Creature, Quest const*_Quest ); + bool (*pGossipSelect )(Player *player, Creature *_Creature, uint32 sender, uint32 action ); + bool (*pGossipSelectWithCode)(Player *player, Creature *_Creature, uint32 sender, uint32 action, const char* sCode ); + bool (*pQuestSelect )(Player *player, Creature *_Creature, Quest const*_Quest ); + bool (*pQuestComplete )(Player *player, Creature *_Creature, Quest const*_Quest ); + uint32 (*pNPCDialogStatus )(Player *player, Creature *_Creature ); + uint32 (*pGODialogStatus )(Player *player, GameObject * _GO ); + bool (*pChooseReward )(Player *player, Creature *_Creature, Quest const*_Quest, uint32 opt ); + bool (*pItemHello )(Player *player, Item *_Item, Quest const*_Quest ); + bool (*pGOHello )(Player *player, GameObject *_GO ); + bool (*pAreaTrigger )(Player *player, AreaTriggerEntry* at); + bool (*pItemQuestAccept )(Player *player, Item *_Item, Quest const*_Quest ); + bool (*pGOQuestAccept )(Player *player, GameObject *_GO, Quest const*_Quest ); + bool (*pGOChooseReward )(Player *player, GameObject *_GO, Quest const*_Quest, uint32 opt ); + bool (*pReceiveEmote )(Player *player, Creature *_Creature, uint32 emote ); + bool (*pItemUse )(Player *player, Item* _Item, SpellCastTargets const& targets); + + CreatureAI* (*GetAI)(Creature *_Creature); + // ----------------------------------------- + +}; + +class InstanceDataScript +{ + public: + InstanceDataScript() : GetInstanceData(NULL) {}; + + std::string name; + InstanceData* (*GetInstanceData)(Map *_Map); +}; + +extern int nrscripts; +extern Script *m_scripts[MAX_SCRIPTS]; +extern InstanceDataScript *m_instance_scripts[MAX_INSTANCE_SCRIPTS]; +extern int num_inst_scripts; + +#define VISIBLE_RANGE (50.0f) + +struct MANGOS_DLL_DECL ScriptedAI : public CreatureAI +{ + ScriptedAI(Creature* creature) : m_creature(creature) {} + ~ScriptedAI() {} + + // Called if IsVisible(Unit *who) is true at each *who move + void MoveInLineOfSight(Unit *) {} + + // Called at each attack of m_creature by any victim + void AttackStart(Unit *) {} + + // Called at stopping attack by any attacker + void EnterEvadeMode(); + + // Called at any heal cast/item used (call non implemented) + void HealBy(Unit* /*healer*/, uint32 /*amount_healed*/) {} + + // Called at any Damage to any victim (before damage apply) + void DamageDeal(Unit* /*done_to*/, uint32& /*damage*/) {} + + // Called at any Damage from any attacker (before damage apply) + void DamageTaken(Unit* /*done_by*/, uint32& /*damage*/) {} + + // Is unit visible for MoveInLineOfSight + bool IsVisible(Unit* who) const + { + return !who->HasStealthAura() && m_creature->GetDistance(who) <= VISIBLE_RANGE; + } + + // Called at World update tick + void UpdateAI(const uint32); + + // Called when the creature is killed + void JustDied(Unit *){} + + // Called when the creature kills a unit + void KilledUnit(Unit *){} + + // Called when hit by a spell + void SpellHit(Unit *, const SpellEntry*){} + + Creature* m_creature; + + //= Some useful helpers ========================= + + // Start attack of victim and go to him + void DoStartAttack(Unit* victim); + + // Stop attack of current victim + void DoStopAttack(); + + // Cast spell + void DoCast(Unit* victim, uint32 spelId) + { + m_creature->CastSpell(victim,spelId,true); + } + + void DoCastSpell(Unit* who,SpellEntry *spellInfo) + { + m_creature->CastSpell(who,spellInfo,true); + } + + void DoSay(char const* text, uint32 language) + { + m_creature->Say(text,language,0); + } + + void DoGoHome(); +}; + +#endif diff --git a/src/bindings/interface/Scripts/sc_default.cpp b/src/bindings/interface/Scripts/sc_default.cpp new file mode 100644 index 00000000000..a37ef14c796 --- /dev/null +++ b/src/bindings/interface/Scripts/sc_default.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sc_defines.h" + +bool GossipHello_default(Player* /*player*/, Creature* /*_Creature*/) +{ + return false; +} + +bool GossipSelect_default(Player* /*player*/, Creature* /*_Creature*/, uint32 /*sender*/, uint32 /*action*/ ) +{ + return false; +} + +bool GossipSelectWithCode_default( Player* /*player*/, Creature* /*_Creature*/, uint32 /*sender*/, uint32 /*action*/, const char* /*sCode*/ ) +{ + return false; +} + +bool QuestAccept_default(Player* /*player*/, Creature* /*_Creature*/, Quest const* /*_Quest*/ ) +{ + return false; +} + +bool QuestSelect_default(Player* /*player*/, Creature* /*_Creature*/, Quest const* /*_Quest*/ ) +{ + return false; +} + +bool QuestComplete_default(Player* /*player*/, Creature* /*_Creature*/, Quest const* /*_Quest*/ ) +{ + return false; +} + +bool ChooseReward_default(Player* /*player*/, Creature* /*_Creature*/, Quest const* /*_Quest*/, uint32 /*opt*/ ) +{ + return false; +} + +uint32 NPCDialogStatus_default(Player* /*player*/, Creature* /*_Creature*/ ) +{ + return 128; +} + +uint32 GODialogStatus_default(Player* /*player*/, GameObject* /*_Creature*/ ) +{ + return 128; +} + +bool ItemHello_default(Player* /*player*/, Item* /*_Item*/, Quest const* /*_Quest*/ ) +{ + return false; +} + +bool ItemQuestAccept_default(Player* /*player*/, Item* /*_Item*/, Quest const* /*_Quest*/ ) +{ + return false; +} + +bool GOHello_default(Player* /*player*/, GameObject* /*_GO*/ ) +{ + return false; +} + +bool GOQuestAccept_default(Player* /*player*/, GameObject* /*_GO*/, Quest const* /*_Quest*/ ) +{ + return false; +} + +bool GOChooseReward_default(Player* /*player*/, GameObject* /*_GO*/, Quest const* /*_Quest*/, uint32 /*opt*/ ) +{ + return false; +} + +bool AreaTrigger_default(Player* /*player*/, AreaTriggerEntry* /*atEntry*/ ) +{ + return false; +} + +void AddSC_default() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="default"; + newscript->pGossipHello = &GossipHello_default; + newscript->pQuestAccept = &QuestAccept_default; + newscript->pGossipSelect = &GossipSelect_default; + newscript->pGossipSelectWithCode = &GossipSelectWithCode_default; + newscript->pQuestSelect = &QuestSelect_default; + newscript->pQuestComplete = &QuestComplete_default; + newscript->pNPCDialogStatus = &NPCDialogStatus_default; + newscript->pGODialogStatus = &GODialogStatus_default; + newscript->pChooseReward = &ChooseReward_default; + newscript->pItemHello = &ItemHello_default; + newscript->pGOHello = &GOHello_default; + newscript->pAreaTrigger = &AreaTrigger_default; + newscript->pItemQuestAccept = &ItemQuestAccept_default; + newscript->pGOQuestAccept = &GOQuestAccept_default; + newscript->pGOChooseReward = &GOChooseReward_default; + + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/interface/Scripts/sc_defines.cpp b/src/bindings/interface/Scripts/sc_defines.cpp new file mode 100644 index 00000000000..0c93b89649b --- /dev/null +++ b/src/bindings/interface/Scripts/sc_defines.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "sc_defines.h" + +#include "../../game/Player.h" + +uint32 GetSkillLevel(Player *player,uint32 trskill) +{ + // Returns the level of some tradetrskill known by player + // Need to add missing spells + + uint32 spell_apprentice = 0; + uint32 spell_journeyman = 0; + uint32 spell_expert = 0; + uint32 spell_artisan = 0; + uint32 spell_master = 0; + + switch(trskill) + { + case TRADESKILL_ALCHEMY: + spell_apprentice = 2259; + spell_journeyman = 3101; + spell_expert = 3464; + spell_artisan = 11611; + spell_master = 28596; // teached by 28597 + break; + case TRADESKILL_BLACKSMITHING: + spell_apprentice = 2018; + spell_journeyman = 3100; + spell_expert = 8768; + spell_artisan = 11454; + spell_master = 29844; // teached by 29845 + break; + case TRADESKILL_COOKING: + spell_apprentice = 2550; + spell_journeyman = 3102; + spell_expert = 3413; + spell_artisan = 18260; + spell_master = 33359; // teached by 33361 + break; + case TRADESKILL_ENCHANTING: + spell_apprentice = 7411; + spell_journeyman = 7412; + spell_expert = 7413; + spell_artisan = 13920; + spell_master = 28029; // teached by 28030 + break; + case TRADESKILL_ENGINEERING: + spell_apprentice = 4036; + spell_journeyman = 4037; + spell_expert = 4038; + spell_artisan = 12656; + spell_master = 30350; // teached by 30351 + break; + case TRADESKILL_FIRSTAID: + spell_apprentice = 3273; + spell_journeyman = 3274; + spell_expert = 7924; + spell_artisan = 10846; + spell_master = 27028; // teached by 27029 + break; + case TRADESKILL_HERBALISM: + spell_apprentice = 2372; + spell_journeyman = 2373; + spell_expert = 3571; + spell_artisan = 11994; + spell_master = 0; + break; + case TRADESKILL_LEATHERWORKING: + spell_apprentice = 2108; + spell_journeyman = 3104; + spell_expert = 20649; + spell_artisan = 10662; + spell_master = 32549; // teached by 32550 + break; + case TRADESKILL_POISONS: + spell_apprentice = 0; + spell_journeyman = 0; + spell_expert = 0; + spell_artisan = 0; + spell_master = 0; + break; + case TRADESKILL_TAILORING: + spell_apprentice = 3908; + spell_journeyman = 3909; + spell_expert = 3910; + spell_artisan = 12180; + spell_master = 26790; // teached by 26791 + break; + case TRADESKILL_MINING: + spell_apprentice = 2581; + spell_journeyman = 2582; + spell_expert = 3568; + spell_artisan = 10249; + spell_master = 29354; // teached by 29355 + break; + case TRADESKILL_FISHING: + spell_apprentice = 7733; + spell_journeyman = 7734; + spell_expert = 7736; + spell_artisan = 18249; + spell_master = 33098; // teached by 33100 + break; + case TRADESKILL_SKINNING: + spell_apprentice = 8615; + spell_journeyman = 8619; + spell_expert = 8620; + spell_artisan = 10769; + spell_master = 32679; // teached by 32678 + break; + case TRADESKILL_JEWELCRAFTING: + spell_apprentice = 25229; // teached by 25245 + spell_journeyman = 25230; // teached by 25246 + spell_expert = 28894; // teached by 28896 + spell_artisan = 28895; // teached by 28899 + spell_master = 28897; // teached by 28901 + break; + } + + if (player->HasSpell(spell_master)) + return TRADESKILL_LEVEL_MASTER; + + if (player->HasSpell(spell_artisan)) + return TRADESKILL_LEVEL_ARTISAN; + + if (player->HasSpell(spell_expert)) + return TRADESKILL_LEVEL_EXPERT; + + if (player->HasSpell(spell_journeyman)) + return TRADESKILL_LEVEL_JOURNEYMAN; + + if (player->HasSpell(spell_apprentice)) + return TRADESKILL_LEVEL_APPRENTICE; + + return TRADESKILL_LEVEL_NONE; +} diff --git a/src/bindings/interface/Scripts/sc_defines.h b/src/bindings/interface/Scripts/sc_defines.h new file mode 100644 index 00000000000..c906726f09d --- /dev/null +++ b/src/bindings/interface/Scripts/sc_defines.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SC_DEFINES_H +#define SC_DEFINES_H + +#include "../ScriptMgr.h" + +// 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_JEWELCRAFTING 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 DEFAULT_GOSSIP_MESSAGE 0xffffff + +extern uint32 GetSkillLevel(Player *player,uint32 skill); + +// Defined functions to use with player. + +#define ADD_GOSSIP_ITEM(a,b,c,d,e,f) PlayerTalkClass->GetGossipMenu()->AddMenuItem(a,b,c,d,e,f) +#define SEND_GOSSIP_MENU(a,b) PlayerTalkClass->SendGossipMenu(a,b) +#define SEND_POI(a,b,c,d,e,f) PlayerTalkClass->SendPointOfInterest(a,b,c,d,e,f) +#define CLOSE_GOSSIP_MENU() PlayerTalkClass->CloseGossip(); + +#define QUEST_DIALOG_STATUS(a,b,c) GetSession()->getDialogStatus(a,b,c) +#define SEND_QUEST_DETAILS(a,b,c) PlayerTalkClass->SendQuestDetails(a,b,c) +#define SEND_REQUESTEDITEMS(a,b,c,d) PlayerTalkClass->SendRequestedItems(a,b,c,d) + +#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) +#define SEND_SPRESURRECT() GetSession()->SendSpiritResurrect() +#define GET_HONORRANK() GetHonorRank() + +// ----------------------------------- +#endif diff --git a/src/bindings/interface/config.h b/src/bindings/interface/config.h new file mode 100644 index 00000000000..68d428a3809 --- /dev/null +++ b/src/bindings/interface/config.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CONFIG_H +#define CONFIG_H +#endif +//#define WIN32 + +#ifdef WIN32 +//#include +#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 diff --git a/src/bindings/interface/system.cpp b/src/bindings/interface/system.cpp new file mode 100644 index 00000000000..ad5548072e6 --- /dev/null +++ b/src/bindings/interface/system.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef WIN32 +#include +BOOL APIENTRY DllMain( HANDLE /*hModule*/, DWORD /*ul_reason_for_call*/, LPVOID /*lpReserved*/) +{ + return true; +} +#endif diff --git a/src/bindings/scripts/How to install.txt b/src/bindings/scripts/How to install.txt new file mode 100644 index 00000000000..98fbf290c08 --- /dev/null +++ b/src/bindings/scripts/How to install.txt @@ -0,0 +1,32 @@ +--- How to install ScriptDev2 --- + +1) Download MaNGOS +2) Create a new folder under "src\bindings\" within the MaNGOS source called "ScriptDev2" +3) Checkout the ScriptDev2 trunk from "https://scriptdev2.svn.sourceforge.net/svnroot/scriptdev2" + +4a) On Win32: + - Compile MaNGOS + - Compile ScriptDev2 using the ScriptVC70, ScriptVC80, or ScriptVC90 Solution within the ScriptDev2 folder (this will overwrite the Mangoscript dll in the output directory) + +4b) On Linux: +- Apply MaNGOS-rXXXX-ScriptDev2.patch, where XXXX is the highest version to that of your MaNGOS revision, to Mangos Source +- Compile MaNGOS (ScriptDev2 will automatically be built when compiling Mangos from here on) + +5) Create the default ScriptDev2 database using "sql\created_database.sql", then execute "sql\scriptdev2_structure.sql" on that database. + +6) Execute the included "sql\scripts_mangosd_full.sql" and "sql\scripts_mangosd_additional_req.sql" on your MaNGOS database + +7) Place the included "scriptdev2.conf" file within the directory containing your "mangosd.conf" and "realmd.conf" files. You may need to change this file to match the database you created and any custom settings you wish to use (such as a different locale). + +8) Run mangosd from your output directory + + +To update ScriptDev2: +All you have to do is open src\bindings\ and right click on the ScriptDev2 folder and click "Update" and then follow steps 4, 6, 7, and 8 again. You must still compile MaNGOS before ScriptDev2 when on the Windows platform. + +To update your Database with new Scriptdev2 SQL changes you can either: +a) apply only the changes that were made during that revision by looking in the sql\update folder or (files named rXXX_scriptdev2.sql should be executed on the scriptdev2 db while rXXX_mangos.sql should be executed on your mangos db) +b) reapply "mangos_full_scripts.sql" to your MaNGOS database along with executing "mangos_additional_req.sql" on your MaNGOS database. + +You can view the ScriptDev2 Change Log at: +[url=http://scriptdev2.svn.sourceforge.net/viewvc/scriptdev2/?view=log]http://scriptdev2.svn.sourceforge.net/viewvc/scriptdev2/?view=log[/url] \ No newline at end of file diff --git a/src/bindings/scripts/LICENSE.txt b/src/bindings/scripts/LICENSE.txt new file mode 100644 index 00000000000..69cd8a1df6d --- /dev/null +++ b/src/bindings/scripts/LICENSE.txt @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/src/bindings/scripts/Makefile.am b/src/bindings/scripts/Makefile.am new file mode 100644 index 00000000000..22f4725e2f6 --- /dev/null +++ b/src/bindings/scripts/Makefile.am @@ -0,0 +1,421 @@ +# Copyright © 2005,2006,2007 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse +## SUBDIRS = sql + +# build shared library +lib_LTLIBRARIES = libtrinityscript.la + +libtrinityscript_la_CPPFLAGS = \ +$(MYSQL_INCLUDES) \ +$(POSTGRE_INCLUDES) \ +-I$(top_srcdir)/dep/include \ +-I$(top_srcdir)/src/shared \ +-I$(top_srcdir)/src/framework \ +-I$(top_srcdir)/src/game \ +-I$(srcdir)/include \ +-D_TRINITY_SCRIPT_CONFIG='"$(sysconfdir)/trinityscript.conf"' + +libtrinityscript_la_SOURCES = \ +ScriptMgr.cpp \ +ScriptMgr.h \ +include/precompiled.cpp \ +include/precompiled.h \ +include/sc_creature.cpp \ +include/sc_creature.h \ +include/sc_gossip.h \ +include/sc_grid_searchers.h \ +include/sc_instance.h \ +include/sc_item.h \ +scripts/areatrigger/areatrigger_scripts.cpp \ +scripts/boss/boss_emeriss.cpp \ +scripts/boss/boss_lethon.cpp \ +scripts/boss/boss_taerar.cpp \ +scripts/boss/boss_ysondre.cpp \ +scripts/creature/mob_event_ai.cpp \ +scripts/creature/mob_event_ai.h \ +scripts/creature/mob_generic_creature.cpp \ +scripts/creature/simple_ai.cpp \ +scripts/creature/simple_ai.h \ +scripts/custom/custom_example.cpp \ +scripts/custom/custom_gossip_codebox.cpp \ +scripts/custom/test.cpp \ +scripts/go/go_scripts.cpp \ +scripts/guard/guard_ai.cpp \ +scripts/guard/guard_ai.h \ +scripts/guard/guards.cpp \ +scripts/item/item_scripts.cpp \ +scripts/item/item_test.cpp \ +scripts/npc/npc_escortAI.cpp \ +scripts/npc/npc_escortAI.h \ +scripts/npc/npc_innkeeper.cpp \ +scripts/npc/npc_professions.cpp \ +scripts/npc/npcs_special.cpp \ +scripts/zone/alterac_mountains/alterac_mountains.cpp \ +scripts/zone/aunchindoun/auchenai_crypts/boss_exarch_maladaar.cpp \ +scripts/zone/aunchindoun/mana_tombs/boss_nexusprince_shaffar.cpp \ +scripts/zone/aunchindoun/mana_tombs/boss_pandemonius.cpp \ +scripts/zone/aunchindoun/sethekk_halls/boss_darkweaver_syth.cpp \ +scripts/zone/aunchindoun/sethekk_halls/boss_tailonking_ikiss.cpp \ +scripts/zone/aunchindoun/sethekk_halls/def_sethekk_halls.h \ +scripts/zone/aunchindoun/sethekk_halls/instance_sethekk_halls.cpp \ +scripts/zone/aunchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp \ +scripts/zone/aunchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp \ +scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp \ +scripts/zone/aunchindoun/shadow_labyrinth/boss_murmur.cpp \ +scripts/zone/aunchindoun/shadow_labyrinth/def_shadow_labyrinth.h \ +scripts/zone/aunchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp \ +scripts/zone/azshara/azshara.cpp \ +scripts/zone/azshara/boss_azuregos.cpp \ +scripts/zone/azuremyst_isle/azuremyst_isle.cpp \ +scripts/zone/barrens/the_barrens.cpp \ +scripts/zone/black_temple/black_temple.cpp \ +scripts/zone/black_temple/boss_bloodboil.cpp \ +scripts/zone/black_temple/boss_illidan.cpp \ +scripts/zone/black_temple/boss_mother_shahraz.cpp \ +scripts/zone/black_temple/boss_reliquary_of_souls.cpp \ +scripts/zone/black_temple/boss_shade_of_akama.cpp \ +scripts/zone/black_temple/boss_supremus.cpp \ +scripts/zone/black_temple/boss_teron_gorefiend.cpp \ +scripts/zone/black_temple/boss_warlord_najentus.cpp \ +scripts/zone/black_temple/def_black_temple.h \ +scripts/zone/black_temple/illidari_council.cpp \ +scripts/zone/black_temple/instance_black_temple.cpp \ +scripts/zone/blackrock_depths/blackrock_depths.cpp \ +scripts/zone/blackrock_depths/boss_ambassador_flamelash.cpp \ +scripts/zone/blackrock_depths/boss_angerrel.cpp \ +scripts/zone/blackrock_depths/boss_anubshiah.cpp \ +scripts/zone/blackrock_depths/boss_doomrel.cpp \ +scripts/zone/blackrock_depths/boss_doperel.cpp \ +scripts/zone/blackrock_depths/boss_emperor_dagran_thaurissan.cpp \ +scripts/zone/blackrock_depths/boss_general_angerforge.cpp \ +scripts/zone/blackrock_depths/boss_gloomrel.cpp \ +scripts/zone/blackrock_depths/boss_gorosh_the_dervish.cpp \ +scripts/zone/blackrock_depths/boss_grizzle.cpp \ +scripts/zone/blackrock_depths/boss_haterel.cpp \ +scripts/zone/blackrock_depths/boss_high_interrogator_gerstahn.cpp \ +scripts/zone/blackrock_depths/boss_magmus.cpp \ +scripts/zone/blackrock_depths/boss_moira_bronzebeard.cpp \ +scripts/zone/blackrock_depths/boss_seethrel.cpp \ +scripts/zone/blackrock_depths/boss_vilerel.cpp \ +scripts/zone/blackrock_spire/boss_drakkisath.cpp \ +scripts/zone/blackrock_spire/boss_gyth.cpp \ +scripts/zone/blackrock_spire/boss_halycon.cpp \ +scripts/zone/blackrock_spire/boss_highlord_omokk.cpp \ +scripts/zone/blackrock_spire/boss_mother_smolderweb.cpp \ +scripts/zone/blackrock_spire/boss_overlord_wyrmthalak.cpp \ +scripts/zone/blackrock_spire/boss_pyroguard_emberseer.cpp \ +scripts/zone/blackrock_spire/boss_quartermaster_zigris.cpp \ +scripts/zone/blackrock_spire/boss_rend_blackhand.cpp \ +scripts/zone/blackrock_spire/boss_shadow_hunter_voshgajin.cpp \ +scripts/zone/blackrock_spire/boss_the_beast.cpp \ +scripts/zone/blackrock_spire/boss_warmaster_voone.cpp \ +scripts/zone/blackwing_lair/boss_broodlord_lashlayer.cpp \ +scripts/zone/blackwing_lair/boss_chromaggus.cpp \ +scripts/zone/blackwing_lair/boss_ebonroc.cpp \ +scripts/zone/blackwing_lair/boss_firemaw.cpp \ +scripts/zone/blackwing_lair/boss_flamegor.cpp \ +scripts/zone/blackwing_lair/boss_nefarian.cpp \ +scripts/zone/blackwing_lair/boss_razorgore.cpp \ +scripts/zone/blackwing_lair/boss_vaelastrasz.cpp \ +scripts/zone/blackwing_lair/boss_victor_nefarius.cpp \ +scripts/zone/blackwing_lair/instance_blackwing_lair.cpp \ +scripts/zone/blades_edge_mountains/blades_edge_mountains.cpp \ +scripts/zone/blasted_lands/blasted_lands.cpp \ +scripts/zone/blasted_lands/boss_kruul.cpp \ +scripts/zone/bloodmyst_isle/bloodmyst_isle.cpp \ +scripts/zone/burning_steppes/burning_steppes.cpp \ +scripts/zone/caverns_of_time/dark_portal/boss_aeonus.cpp \ +scripts/zone/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp \ +scripts/zone/caverns_of_time/dark_portal/boss_temporus.cpp \ +scripts/zone/caverns_of_time/hyjal/boss_archimonde.cpp \ +scripts/zone/caverns_of_time/hyjal/def_hyjal.h \ +scripts/zone/caverns_of_time/hyjal/hyjal.cpp \ +scripts/zone/caverns_of_time/hyjal/hyjalAI.cpp \ +scripts/zone/caverns_of_time/hyjal/hyjalAI.h \ +scripts/zone/caverns_of_time/hyjal/instance_hyjal.cpp \ +scripts/zone/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp \ +scripts/zone/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp \ +scripts/zone/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp \ +scripts/zone/caverns_of_time/old_hillsbrad/def_old_hillsbrad.h \ +scripts/zone/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp \ +scripts/zone/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp \ +scripts/zone/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp \ +scripts/zone/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp \ +scripts/zone/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp \ +scripts/zone/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp \ +scripts/zone/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp \ +scripts/zone/coilfang_resevoir/serpent_shrine/def_serpent_shrine.h \ +scripts/zone/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp \ +scripts/zone/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp \ +scripts/zone/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp \ +scripts/zone/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp \ +scripts/zone/coilfang_resevoir/steam_vault/def_steam_vault.h \ +scripts/zone/coilfang_resevoir/steam_vault/instance_steam_vault.cpp \ +scripts/zone/coilfang_resevoir/underbog/boss_hungarfen.cpp \ +scripts/zone/darkshore/darkshore.cpp \ +scripts/zone/deadmines/deadmines.cpp \ +scripts/zone/dun_morogh/dun_morogh.cpp \ +scripts/zone/dustwallow_marsh/dustwallow_marsh.cpp \ +scripts/zone/eastern_plaguelands/eastern_plaguelands.cpp \ +scripts/zone/elwynn_forest/elwynn_forest.cpp \ +scripts/zone/eversong_woods/eversong_woods.cpp \ +scripts/zone/felwood/felwood.cpp \ +scripts/zone/feralas/feralas.cpp \ +scripts/zone/ghostlands/ghostlands.cpp \ +scripts/zone/gruuls_lair/boss_gruul.cpp \ +scripts/zone/gruuls_lair/boss_high_king_maulgar.cpp \ +scripts/zone/gruuls_lair/def_gruuls_lair.h \ +scripts/zone/gruuls_lair/instance_gruuls_lair.cpp \ +scripts/zone/hellfire_citadel/blood_furnace/boss_broggok.cpp \ +scripts/zone/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp \ +scripts/zone/hellfire_citadel/blood_furnace/boss_the_maker.cpp \ +scripts/zone/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp \ +scripts/zone/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp \ +scripts/zone/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp \ +scripts/zone/hellfire_citadel/magtheridons_lair/def_magtheridons_lair.h \ +scripts/zone/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp \ +scripts/zone/hellfire_citadel/shattered_halls/boss_nethekurse.cpp \ +scripts/zone/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp \ +scripts/zone/hellfire_citadel/shattered_halls/def_shattered_halls.h \ +scripts/zone/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp \ +scripts/zone/hellfire_peninsula/boss_doomlord_kazzak.cpp \ +scripts/zone/hellfire_peninsula/hellfire_peninsula.cpp \ +scripts/zone/ironforge/ironforge.cpp \ +scripts/zone/isle_of_queldanas/isle_of_queldanas.cpp \ +scripts/zone/karazhan/boss_curator.cpp \ +scripts/zone/karazhan/boss_maiden_of_virtue.cpp \ +scripts/zone/karazhan/boss_midnight.cpp \ +scripts/zone/karazhan/boss_moroes.cpp \ +scripts/zone/karazhan/boss_netherspite.cpp \ +scripts/zone/karazhan/boss_nightbane.cpp \ +scripts/zone/karazhan/boss_prince_malchezaar.cpp \ +scripts/zone/karazhan/boss_shade_of_aran.cpp \ +scripts/zone/karazhan/boss_terestian_illhoof.cpp \ +scripts/zone/karazhan/bosses_opera.cpp \ +scripts/zone/karazhan/def_karazhan.h \ +scripts/zone/karazhan/instance_karazhan.cpp \ +scripts/zone/karazhan/karazhan.cpp \ +scripts/zone/loch_modan/loch_modan.cpp \ +scripts/zone/magisters_terrace/boss_felblood_kaelthas.cpp \ +scripts/zone/magisters_terrace/boss_priestess_delrissa.cpp \ +scripts/zone/magisters_terrace/boss_selin_fireheart.cpp \ +scripts/zone/magisters_terrace/boss_vexallus.cpp \ +scripts/zone/magisters_terrace/def_magisters_terrace.h \ +scripts/zone/magisters_terrace/instance_magisters_terrace.cpp \ +scripts/zone/maraudon/boss_celebras_the_cursed.cpp \ +scripts/zone/maraudon/boss_landslide.cpp \ +scripts/zone/maraudon/boss_noxxion.cpp \ +scripts/zone/maraudon/boss_princess_theradras.cpp \ +scripts/zone/molten_core/boss_baron_geddon.cpp \ +scripts/zone/molten_core/boss_garr.cpp \ +scripts/zone/molten_core/boss_gehennas.cpp \ +scripts/zone/molten_core/boss_golemagg.cpp \ +scripts/zone/molten_core/boss_lucifron.cpp \ +scripts/zone/molten_core/boss_magmadar.cpp \ +scripts/zone/molten_core/boss_majordomo_executus.cpp \ +scripts/zone/molten_core/boss_ragnaros.cpp \ +scripts/zone/molten_core/boss_shazzrah.cpp \ +scripts/zone/molten_core/boss_sulfuron_harbinger.cpp \ +scripts/zone/molten_core/def_molten_core.h \ +scripts/zone/molten_core/instance_molten_core.cpp \ +scripts/zone/molten_core/molten_core.cpp \ +scripts/zone/moonglade/moonglade.cpp \ +scripts/zone/mulgore/mulgore.cpp \ +scripts/zone/nagrand/nagrand.cpp \ +scripts/zone/naxxramas/boss_anubrekhan.cpp \ +scripts/zone/naxxramas/boss_faerlina.cpp \ +scripts/zone/naxxramas/boss_feugen.cpp \ +scripts/zone/naxxramas/boss_gluth.cpp \ +scripts/zone/naxxramas/boss_gothik.cpp \ +scripts/zone/naxxramas/boss_grobbulus.cpp \ +scripts/zone/naxxramas/boss_heigan.cpp \ +scripts/zone/naxxramas/boss_highlord_mograine.cpp \ +scripts/zone/naxxramas/boss_kelthuzad.cpp \ +scripts/zone/naxxramas/boss_lady_blaumeux.cpp \ +scripts/zone/naxxramas/boss_loatheb.cpp \ +scripts/zone/naxxramas/boss_maexxna.cpp \ +scripts/zone/naxxramas/boss_noth.cpp \ +scripts/zone/naxxramas/boss_patchwerk.cpp \ +scripts/zone/naxxramas/boss_razuvious.cpp \ +scripts/zone/naxxramas/boss_sapphiron.cpp \ +scripts/zone/naxxramas/boss_sir_zeliek.cpp \ +scripts/zone/naxxramas/boss_stalagg.cpp \ +scripts/zone/naxxramas/boss_thaddius.cpp \ +scripts/zone/naxxramas/boss_thane_korthazz.cpp \ +scripts/zone/naxxramas/instance_naxxramas.cpp \ +scripts/zone/netherstorm/netherstorm.cpp \ +scripts/zone/onyxias_lair/boss_onyxia.cpp \ +scripts/zone/orgrimmar/orgrimmar.cpp \ +scripts/zone/razorfen_downs/boss_amnennar_the_coldbringer.cpp \ +scripts/zone/ruins_of_ahnqiraj/boss_ayamiss.cpp \ +scripts/zone/ruins_of_ahnqiraj/boss_buru.cpp \ +scripts/zone/ruins_of_ahnqiraj/boss_kurinnaxx.cpp \ +scripts/zone/ruins_of_ahnqiraj/boss_moam.cpp \ +scripts/zone/ruins_of_ahnqiraj/boss_ossirian.cpp \ +scripts/zone/ruins_of_ahnqiraj/boss_rajaxx.cpp \ +scripts/zone/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp \ +scripts/zone/scarlet_monastery/boss_arcanist_doan.cpp \ +scripts/zone/scarlet_monastery/boss_azshir_the_sleepless.cpp \ +scripts/zone/scarlet_monastery/boss_bloodmage_thalnos.cpp \ +scripts/zone/scarlet_monastery/boss_herod.cpp \ +scripts/zone/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp \ +scripts/zone/scarlet_monastery/boss_high_inquisitor_whitemane.cpp \ +scripts/zone/scarlet_monastery/boss_houndmaster_loksey.cpp \ +scripts/zone/scarlet_monastery/boss_interrogator_vishas.cpp \ +scripts/zone/scarlet_monastery/boss_scarlet_commander_mograine.cpp \ +scripts/zone/scarlet_monastery/boss_scorn.cpp \ +scripts/zone/scholomance/boss_darkmaster_gandling.cpp \ +scripts/zone/scholomance/boss_death_knight_darkreaver.cpp \ +scripts/zone/scholomance/boss_doctor_theolen_krastinov.cpp \ +scripts/zone/scholomance/boss_illucia_barov.cpp \ +scripts/zone/scholomance/boss_instructor_malicia.cpp \ +scripts/zone/scholomance/boss_jandice_barov.cpp \ +scripts/zone/scholomance/boss_kormok.cpp \ +scripts/zone/scholomance/boss_lord_alexei_barov.cpp \ +scripts/zone/scholomance/boss_lorekeeper_polkelt.cpp \ +scripts/zone/scholomance/boss_ras_frostwhisper.cpp \ +scripts/zone/scholomance/boss_the_ravenian.cpp \ +scripts/zone/scholomance/boss_vectus.cpp \ +scripts/zone/scholomance/def_scholomance.h \ +scripts/zone/scholomance/instance_scholomance.cpp \ +scripts/zone/searing_gorge/searing_gorge.cpp \ +scripts/zone/shadowfang_keep/def_shadowfang_keep.h \ +scripts/zone/shadowfang_keep/instance_shadowfang_keep.cpp \ +scripts/zone/shadowfang_keep/shadowfang_keep.cpp \ +scripts/zone/shadowmoon_valley/boss_doomwalker.cpp \ +scripts/zone/shadowmoon_valley/shadowmoon_valley.cpp \ +scripts/zone/shattrath/shattrath_city.cpp \ +scripts/zone/silithus/silithus.cpp \ +scripts/zone/silvermoon/silvermoon_city.cpp \ +scripts/zone/silverpine_forest/silverpine_forest.cpp \ +scripts/zone/stonetalon_mountains/stonetalon_mountains.cpp \ +scripts/zone/stormwind/stormwind_city.cpp \ +scripts/zone/stranglethorn_vale/stranglethorn_vale.cpp \ +scripts/zone/stratholme/boss_baron_rivendare.cpp \ +scripts/zone/stratholme/boss_baroness_anastari.cpp \ +scripts/zone/stratholme/boss_cannon_master_willey.cpp \ +scripts/zone/stratholme/boss_dathrohan_balnazzar.cpp \ +scripts/zone/stratholme/boss_magistrate_barthilas.cpp \ +scripts/zone/stratholme/boss_maleki_the_pallid.cpp \ +scripts/zone/stratholme/boss_nerubenkan.cpp \ +scripts/zone/stratholme/boss_order_of_silver_hand.cpp \ +scripts/zone/stratholme/boss_postmaster_malown.cpp \ +scripts/zone/stratholme/boss_ramstein_the_gorger.cpp \ +scripts/zone/stratholme/boss_timmy_the_cruel.cpp \ +scripts/zone/stratholme/def_stratholme.h \ +scripts/zone/stratholme/instance_stratholme.cpp \ +scripts/zone/stratholme/stratholme.cpp \ +scripts/zone/sunwell_plateau/boss_brutallus.cpp \ +scripts/zone/sunwell_plateau/boss_kalecgos.cpp \ +scripts/zone/sunwell_plateau/def_sunwell_plateau.h \ +scripts/zone/sunwell_plateau/instance_sunwell_plateau.cpp \ +scripts/zone/tanaris/tanaris.cpp \ +scripts/zone/tempest_keep/arcatraz/arcatraz.cpp \ +scripts/zone/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp \ +scripts/zone/tempest_keep/arcatraz/def_arcatraz.h \ +scripts/zone/tempest_keep/arcatraz/instance_arcatraz.cpp \ +scripts/zone/tempest_keep/botanica/boss_high_botanist_freywinn.cpp \ +scripts/zone/tempest_keep/botanica/boss_laj.cpp \ +scripts/zone/tempest_keep/botanica/boss_warp_splinter.cpp \ +scripts/zone/tempest_keep/the_eye/boss_astromancer.cpp \ +scripts/zone/tempest_keep/the_eye/boss_kaelthas.cpp \ +scripts/zone/tempest_keep/the_eye/boss_void_reaver.cpp \ +scripts/zone/tempest_keep/the_eye/def_the_eye.h \ +scripts/zone/tempest_keep/the_eye/instance_the_eye.cpp \ +scripts/zone/tempest_keep/the_eye/the_eye.cpp \ +scripts/zone/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp \ +scripts/zone/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp \ +scripts/zone/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp \ +scripts/zone/temple_of_ahnqiraj/boss_bug_trio.cpp \ +scripts/zone/temple_of_ahnqiraj/boss_cthun.cpp \ +scripts/zone/temple_of_ahnqiraj/boss_fankriss.cpp \ +scripts/zone/temple_of_ahnqiraj/boss_huhuran.cpp \ +scripts/zone/temple_of_ahnqiraj/boss_ouro.cpp \ +scripts/zone/temple_of_ahnqiraj/boss_sartura.cpp \ +scripts/zone/temple_of_ahnqiraj/boss_skeram.cpp \ +scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp \ +scripts/zone/temple_of_ahnqiraj/boss_viscidus.cpp \ +scripts/zone/temple_of_ahnqiraj/def_temple_of_ahnqiraj.h \ +scripts/zone/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp \ +scripts/zone/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp \ +scripts/zone/terokkar_forest/terokkar_forest.cpp \ +scripts/zone/thunder_bluff/thunder_bluff.cpp \ +scripts/zone/tirisfal_glades/tirisfal_glades.cpp \ +scripts/zone/uldaman/boss_ironaya.cpp \ +scripts/zone/uldaman/uldaman.cpp \ +scripts/zone/undercity/undercity.cpp \ +scripts/zone/wailing_caverns/instance_wailing_caverns.cpp \ +scripts/zone/western_plaguelands/western_plaguelands.cpp \ +scripts/zone/winterspring/winterspring.cpp \ +scripts/zone/zangarmarsh/zangarmarsh.cpp \ +scripts/zone/zulaman/boss_janalai.cpp \ +scripts/zone/zulaman/boss_nalorakk.cpp \ +scripts/zone/zulaman/def_zulaman.h \ +scripts/zone/zulaman/instance_zulaman.cpp \ +scripts/zone/zulaman/zulaman.cpp \ +scripts/zone/zulfarrak/zulfarrak.cpp \ +scripts/zone/zulgurub/boss_arlokk.cpp \ +scripts/zone/zulgurub/boss_gahzranka.cpp \ +scripts/zone/zulgurub/boss_grilek.cpp \ +scripts/zone/zulgurub/boss_hakkar.cpp \ +scripts/zone/zulgurub/boss_hazzarah.cpp \ +scripts/zone/zulgurub/boss_jeklik.cpp \ +scripts/zone/zulgurub/boss_jindo.cpp \ +scripts/zone/zulgurub/boss_mandokir.cpp \ +scripts/zone/zulgurub/boss_marli.cpp \ +scripts/zone/zulgurub/boss_renataki.cpp \ +scripts/zone/zulgurub/boss_thekal.cpp \ +scripts/zone/zulgurub/boss_venoxis.cpp \ +scripts/zone/zulgurub/boss_wushoolay.cpp \ +scripts/zone/zulgurub/def_zulgurub.h \ +scripts/zone/zulgurub/instance_zulgurub.cpp \ +system.cpp + +## libtool settings +# API versioning +# Increase the last number, if you do bug fixes only, no interface change. +# Increase the middle number when you augmented the interface ( aka add new exported functions ). +# Increase the first number when you break old interface. ( aka remove/change previously exported functions ). +libtrinityscript_la_LIBFLAGS = -version-info 0:0:1 +libtrinityscript_la_LIBADD = $(MYSQL_LIBS) $(POSTGRE_LIBS) + +## Additional files to include when running 'make dist' +# Scripts defaults. +EXTRA_DIST = \ + Scripts/sc_default.cpp \ + Scripts/sc_defines.cpp \ + Scripts/sc_defines.h + +## Additional files to install +sysconf_DATA = \ + trinityscript.conf.dist + +## Prevend overwrite of the config file, if its already installed +install-data-hook: + @list='$(sysconf_DATA)'; for p in $$list; do \ + dest=`echo $$p | sed -e s/.dist//`; \ + if test -f $(DESTDIR)$(sysconfdir)/$$dest; then \ + echo "$@ will not overwrite existing $(DESTDIR)$(sysconfdir)/$$dest"; \ + else \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$dest"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(sysconfdir)/$$dest; \ + fi; \ + done + diff --git a/src/bindings/scripts/ScriptMgr.cpp b/src/bindings/scripts/ScriptMgr.cpp new file mode 100644 index 00000000000..4768a614e5d --- /dev/null +++ b/src/bindings/scripts/ScriptMgr.cpp @@ -0,0 +1,2113 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#include "precompiled.h" +#include "Config/Config.h" +#include "ProgressBar.h" +#include "Database/DBCStores.h" +#include "Database/DatabaseMysql.h" +#include "ObjectMgr.h" +#include "scripts/creature/mob_event_ai.h" + +#define _FULLVERSION "TrinityScript" + +#ifndef _TSCRIPTCONFVERSION +# define _TSCRIPTCONFVERSION 2008100201 +#endif //_TSCRIPTCONFVERSION + +#ifndef _TRINITY_SCRIPT_CONFIG +# define _TRINITY_SCRIPT_CONFIG "trinityscript.conf" +#endif //_TRINITY_SCRIPT_CONFIG + +//*** Global data *** +int nrscripts; +Script *m_scripts[MAX_SCRIPTS]; + +// Text Map for Event AI +HM_NAMESPACE::hash_map EventAI_Text_Map; + +// Script Text used as says / yells / text emotes / whispers in scripts. +struct ScriptText +{ + uint32 SoundId; + uint8 Type; + uint32 Language; + std::string Text; +}; + +// Enums used by ScriptText::Type +enum ChatType +{ + CHAT_TYPE_SAY = 0, + CHAT_TYPE_YELL = 1, + CHAT_TYPE_TEXT_EMOTE = 2, + CHAT_TYPE_BOSS_EMOTE = 3, + CHAT_TYPE_WHISPER = 4, + CHAT_TYPE_BOSS_WHISPER = 5, +}; + +HM_NAMESPACE::hash_map Script_TextMap; + +// Localized Text structure for storing locales (for EAI and SD2 scripts). +struct Localized_Text +{ + std::string locale_1; + std::string locale_2; + std::string locale_3; + std::string locale_4; + std::string locale_5; + std::string locale_6; + std::string locale_7; + std::string locale_8; +}; +HM_NAMESPACE::hash_map EventAI_LocalizedTextMap; +HM_NAMESPACE::hash_map Script_LocalizedTextMap; + +//*** End Global data *** + +//*** EventAI data *** +//Event AI structure. Used exclusivly by mob_event_ai.cpp (60 bytes each) +std::list EventAI_Event_List; + +//Event AI summon structure. Used exclusivly by mob_event_ai.cpp. +HM_NAMESPACE::hash_map EventAI_Summon_Map; + +//Event AI error prevention structure. Used at runtime to prevent error log spam of same creature id. +//HM_NAMESPACE::hash_map EventAI_CreatureErrorPreventionList; + +uint32 EAI_ErrorLevel; + +//*** End EventAI data *** + +DatabaseMysql ScriptDev2DB; +Config SD2Config; +uint32 Locale; + +void FillSpellSummary(); + +// -- Scripts to be added -- + +// -- Areatrigger -- +extern void AddSC_areatrigger_scripts(); + +// -- Boss -- +extern void AddSC_boss_emeriss(); +extern void AddSC_boss_taerar(); +extern void AddSC_boss_ysondre(); + +// -- Creature -- +extern void AddSC_mob_event(); +extern void AddSC_generic_creature(); + +// -- Custom -- +extern void AddSC_custom_example(); +extern void AddSC_custom_gossip_codebox(); +extern void AddSC_test(); + +// -- GO -- +extern void AddSC_go_scripts(); + +// -- Guard -- +extern void AddSC_guards(); + +// -- Honor -- + +// -- Item -- +extern void AddSC_item_scripts(); +extern void AddSC_item_test(); + +// -- NPC -- +extern void AddSC_npc_professions(); +extern void AddSC_npcs_special(); + +// -- Servers -- + +//-------------------- +//------ ZONE -------- + +//Alterac Mountains +extern void AddSC_alterac_mountains(); + +//Arathi Highlands +//Ashenvale Forest +//Aunchindoun +//--Auchenai Crypts +extern void AddSC_boss_exarch_maladaar(); +//--Mana Tombs +extern void AddSC_boss_nexusprince_shaffar(); +extern void AddSC_boss_pandemonius(); + +//--Sekketh Halls +extern void AddSC_boss_darkweaver_syth(); +extern void AddSC_boss_talon_king_ikiss(); +extern void AddSC_instance_sethekk_halls(); + +//--Shadow Labyrinth +extern void AddSC_boss_ambassador_hellmaw(); +extern void AddSC_boss_blackheart_the_inciter(); +extern void AddSC_boss_grandmaster_vorpil(); +extern void AddSC_boss_murmur(); +extern void AddSC_instance_shadow_labyrinth(); + +//Azshara +extern void AddSC_boss_azuregos(); +extern void AddSC_azshara(); + +//Azuremyst Isle +extern void AddSC_azuremyst_isle(); + +//Badlands +//Barrens +extern void AddSC_the_barrens(); + +//Black Temple +extern void AddSC_black_temple(); +extern void AddSC_boss_illidan(); +extern void AddSC_boss_shade_of_akama(); +extern void AddSC_boss_supremus(); +extern void AddSC_boss_gurtogg_bloodboil(); +extern void AddSC_boss_mother_shahraz(); +extern void AddSC_boss_reliquary_of_souls(); +extern void AddSC_boss_teron_gorefiend(); +extern void AddSC_boss_najentus(); +extern void AddSC_boss_illidari_council(); +extern void AddSC_instance_black_temple(); + +//Blackfathom Depths +//Blackrock Depths +extern void AddSC_blackrock_depths(); +extern void AddSC_boss_ambassador_flamelash(); +extern void AddSC_boss_angerrel(); +extern void AddSC_boss_anubshiah(); +extern void AddSC_boss_doomrel(); +extern void AddSC_boss_doperel(); +extern void AddSC_boss_draganthaurissan(); +extern void AddSC_boss_general_angerforge(); +extern void AddSC_boss_gloomrel(); +extern void AddSC_boss_gorosh_the_dervish(); +extern void AddSC_boss_grizzle(); +extern void AddSC_boss_haterel(); +extern void AddSC_boss_high_interrogator_gerstahn(); +extern void AddSC_boss_magmus(); +extern void AddSC_boss_moira_bronzebeard(); +extern void AddSC_boss_seethrel(); +extern void AddSC_boss_vilerel(); + +//Blackrock Spire +extern void AddSC_boss_drakkisath(); +extern void AddSC_boss_halycon(); +extern void AddSC_boss_highlordomokk(); +extern void AddSC_boss_mothersmolderweb(); +extern void AddSC_boss_overlordwyrmthalak(); +extern void AddSC_boss_shadowvosh(); +extern void AddSC_boss_thebeast(); +extern void AddSC_boss_warmastervoone(); +extern void AddSC_boss_quatermasterzigris(); +extern void AddSC_boss_pyroguard_emberseer(); +extern void AddSC_boss_gyth(); +extern void AddSC_boss_rend_blackhand(); + +//Blackwing lair +extern void AddSC_boss_razorgore(); +extern void AddSC_boss_vael(); +extern void AddSC_boss_broodlord(); +extern void AddSC_boss_firemaw(); +extern void AddSC_boss_ebonroc(); +extern void AddSC_boss_flamegor(); +extern void AddSC_boss_chromaggus(); +extern void AddSC_boss_nefarian(); +extern void AddSC_boss_victor_nefarius(); + +//Blade's Edge Mountains +extern void AddSC_blades_edge_mountains(); + +//Blasted lands +extern void AddSC_boss_kruul(); +extern void AddSC_blasted_lands(); + +//Bloodmyst Isle +extern void AddSC_bloodmyst_isle(); + +//Burning steppes +extern void AddSC_burning_steppes(); + +//Caverns of Time +//--Battle for Mt. Hyjal +extern void AddSC_hyjal(); +extern void AddSC_boss_archimonde(); +extern void AddSC_instance_mount_hyjal(); + +//--Old Hillsbrad +extern void AddSC_boss_captain_skarloc(); +extern void AddSC_boss_epoch_hunter(); +extern void AddSC_boss_lieutenant_drake(); +extern void AddSC_instance_old_hillsbrad(); +extern void AddSC_old_hillsbrad(); + +//--The Dark Portal +extern void AddSC_boss_aeonus(); +extern void AddSC_boss_chrono_lord_deja(); +extern void AddSC_boss_temporus(); + +//Coilfang Resevoir +//--Serpent Shrine Cavern +extern void AddSC_boss_fathomlord_karathress(); +extern void AddSC_boss_hydross_the_unstable(); +extern void AddSC_boss_lady_vashj(); +extern void AddSC_boss_leotheras_the_blind(); +extern void AddSC_boss_morogrim_tidewalker(); +extern void AddSC_instance_serpentshrine_cavern(); + +//--Slave Pens + +//--Steam Vault +extern void AddSC_boss_hydromancer_thespia(); +extern void AddSC_boss_mekgineer_steamrigger(); +extern void AddSC_boss_warlord_kalithresh(); +extern void AddSC_instance_steam_vault(); + +//--Underbog +extern void AddSC_boss_hungarfen(); + +//Darkshore +//Darnassus +//Deadmines +//Deadwind pass +//Desolace +//Dire Maul +//Dun Morogh +extern void AddSC_dun_morogh(); + +//Durotar +//Duskwood +//Dustwallow marsh +extern void AddSC_dustwallow_marsh(); + +//Eversong Woods +extern void AddSC_eversong_woods(); + +//Exodar +//Eastern Plaguelands +extern void AddSC_eastern_plaguelands(); + +//Elwynn Forest +extern void AddSC_elwynn_forest(); + +//Felwood +extern void AddSC_felwood(); + +//Feralas +extern void AddSC_feralas(); + +//Ghostlands +extern void AddSC_ghostlands(); + +//Gnomeregan +//Gruul's Lair +extern void AddSC_boss_gruul(); +extern void AddSC_boss_high_king_maulgar(); +extern void AddSC_instance_gruuls_lair(); + +//Hellfire Citadel +//--Blood Furnace +extern void AddSC_boss_broggok(); +extern void AddSC_boss_kelidan_the_breaker(); +extern void AddSC_boss_the_maker(); + +//--Magtheridon's Lair +extern void AddSC_boss_magtheridon(); +extern void AddSC_instance_magtheridons_lair(); + +//--Shattered Halls +extern void AddSC_boss_grand_warlock_nethekurse(); +extern void AddSC_boss_warbringer_omrogg(); +extern void AddSC_instance_shattered_halls(); + +//--Ramparts +extern void AddSC_boss_watchkeeper_gargolmar(); +extern void AddSC_boss_omor_the_unscarred(); + +//Hellfire Peninsula +extern void AddSC_boss_doomlordkazzak(); +extern void AddSC_hellfire_peninsula(); + +//Hillsbrad Foothills +//Hinterlands +//Ironforge +extern void AddSC_ironforge(); + +//Isle of Quel'Danas +extern void AddSC_isle_of_queldanas(); + +//Karazhan +extern void AddSC_boss_attumen(); +extern void AddSC_boss_curator(); +extern void AddSC_boss_maiden_of_virtue(); +extern void AddSC_boss_shade_of_aran(); +extern void AddSC_boss_malchezaar(); +extern void AddSC_boss_terestian_illhoof(); +extern void AddSC_netherspite_infernal(); +extern void AddSC_boss_moroes(); +extern void AddSC_bosses_opera(); +extern void AddSC_instance_karazhan(); +extern void AddSC_karazhan(); + +//Loch Modan +extern void AddSC_loch_modan(); + +//Lower Blackrock Spire + +// Magister's Terrace +extern void AddSC_boss_felblood_kaelthas(); +extern void AddSC_boss_selin_fireheart(); +extern void AddSC_boss_vexallus(); +extern void AddSC_boss_priestess_delrissa(); +extern void AddSC_instance_magisters_terrace(); + +//Maraudon +extern void AddSC_boss_celebras_the_cursed(); +extern void AddSC_boss_landslide(); +extern void AddSC_boss_noxxion(); +extern void AddSC_boss_ptheradras(); + +//Molten core +extern void AddSC_boss_lucifron(); +extern void AddSC_boss_magmadar(); +extern void AddSC_boss_gehennas(); +extern void AddSC_boss_garr(); +extern void AddSC_boss_baron_geddon(); +extern void AddSC_boss_shazzrah(); +extern void AddSC_boss_golemagg(); +extern void AddSC_boss_sulfuron(); +extern void AddSC_boss_majordomo(); +extern void AddSC_boss_ragnaros(); +extern void AddSC_instance_molten_core(); +extern void AddSC_molten_core(); + +//Moonglade +extern void AddSC_moonglade(); + +//Mulgore +extern void AddSC_mulgore(); + +//Nagrand +extern void AddSC_nagrand(); + +//Naxxramas +extern void AddSC_boss_anubrekhan(); +extern void AddSC_boss_maexxna(); +extern void AddSC_boss_patchwerk(); +extern void AddSC_boss_razuvious(); +extern void AddSC_boss_highlord_mograine(); +extern void AddSC_boss_lady_blaumeux(); +extern void AddSC_boss_sir_zeliek(); +extern void AddSC_boss_thane_korthazz(); +extern void AddSC_boss_kelthuzad(); +extern void AddSC_boss_faerlina(); +extern void AddSC_boss_loatheb(); +extern void AddSC_boss_noth(); +extern void AddSC_boss_gluth(); +extern void AddSC_boss_sapphiron(); + +//Netherstorm +extern void AddSC_netherstorm(); + +//Onyxia's Lair +extern void AddSC_boss_onyxia(); + +//Orgrimmar +extern void AddSC_orgrimmar(); + +//Ragefire Chasm +//Razorfen Downs +extern void AddSC_boss_amnennar_the_coldbringer(); + +//Redridge Mountains +//Ruins of Ahn'Qiraj +//Scarlet Monastery +extern void AddSC_boss_arcanist_doan(); +extern void AddSC_boss_azshir_the_sleepless(); +extern void AddSC_boss_bloodmage_thalnos(); +extern void AddSC_boss_herod(); +extern void AddSC_boss_high_inquisitor_fairbanks(); +extern void AddSC_boss_high_inquisitor_whitemane(); +extern void AddSC_boss_houndmaster_loksey(); +extern void AddSC_boss_interrogator_vishas(); +extern void AddSC_boss_scarlet_commander_mograine(); +extern void AddSC_boss_scorn(); + +//Scholomance +extern void AddSC_boss_darkmaster_gandling(); +extern void AddSC_boss_death_knight_darkreaver(); +extern void AddSC_boss_theolenkrastinov(); +extern void AddSC_boss_illuciabarov(); +extern void AddSC_boss_instructormalicia(); +extern void AddSC_boss_jandicebarov(); +extern void AddSC_boss_kormok(); +extern void AddSC_boss_lordalexeibarov(); +extern void AddSC_boss_lorekeeperpolkelt(); +extern void AddSC_boss_rasfrost(); +extern void AddSC_boss_theravenian(); +extern void AddSC_boss_vectus(); +extern void AddSC_instance_scholomance(); + +//Searing gorge +extern void AddSC_searing_gorge(); + +//Shadowfang keep +extern void AddSC_shadowfang_keep(); +extern void AddSC_instance_shadowfang_keep(); + +//Shadowmoon Valley +extern void AddSC_boss_doomwalker(); +extern void AddSC_shadowmoon_valley(); + +//Shattrath +extern void AddSC_shattrath_city(); + +//Silithus +extern void AddSC_silithus(); + +//Silvermoon +extern void AddSC_silvermoon_city(); + +//Silverpine forest +extern void AddSC_silverpine_forest(); + +//Stockade +//Stonetalon mountains +extern void AddSC_stonetalon_mountains(); + +//Stormwind City +extern void AddSC_stormwind_city(); + +//Stranglethorn Vale +extern void AddSC_stranglethorn_vale(); + +//Stratholme +extern void AddSC_boss_magistrate_barthilas(); +extern void AddSC_boss_maleki_the_pallid(); +extern void AddSC_boss_nerubenkan(); +extern void AddSC_boss_cannon_master_willey(); +extern void AddSC_boss_baroness_anastari(); +extern void AddSC_boss_ramstein_the_gorger(); +extern void AddSC_boss_timmy_the_cruel(); +extern void AddSC_boss_postmaster_malown(); +extern void AddSC_boss_baron_rivendare(); +extern void AddSC_boss_dathrohan_balnazzar(); +extern void AddSC_boss_order_of_silver_hand(); +extern void AddSC_instance_stratholme(); +extern void AddSC_stratholme(); + +//Sunken Temple +//Tanaris +extern void AddSC_tanaris(); + +//Teldrassil +//Tempest Keep +//--Arcatraz +extern void AddSC_arcatraz(); +extern void AddSC_boss_harbinger_skyriss(); +extern void AddSC_instance_arcatraz(); + +//--Botanica +extern void AddSC_boss_high_botanist_freywinn(); +extern void AddSC_boss_laj(); +extern void AddSC_boss_warp_splinter(); + +//--The Eye +extern void AddSC_boss_kaelthas(); +extern void AddSC_boss_void_reaver(); +extern void AddSC_boss_high_astromancer_solarian(); +extern void AddSC_instance_the_eye(); +extern void AddSC_the_eye(); + +//--The Mechanar +extern void AddSC_boss_gatewatcher_iron_hand(); +extern void AddSC_boss_nethermancer_sepethrea(); + +//Temple of ahn'qiraj +extern void AddSC_boss_cthun(); +extern void AddSC_boss_fankriss(); +extern void AddSC_boss_huhuran(); +extern void AddSC_bug_trio(); +extern void AddSC_boss_sartura(); +extern void AddSC_boss_skeram(); +extern void AddSC_boss_twinemperors(); +extern void AddSC_mob_anubisath_sentinel(); +extern void AddSC_instance_temple_of_ahnqiraj(); + +//Terokkar Forest +extern void AddSC_terokkar_forest(); + +//Thousand Needles +//Thunder Bluff +extern void AddSC_thunder_bluff(); + +//Tirisfal Glades +extern void AddSC_tirisfal_glades(); + +//Uldaman +extern void AddSC_boss_ironaya(); +extern void AddSC_uldaman(); + +//Undercity +extern void AddSC_undercity(); + +//Un'Goro Crater +//Upper blackrock spire +//Wailing caverns + +//Western plaguelands +extern void AddSC_western_plaguelands(); + +//Westfall +//Wetlands +//Winterspring +extern void AddSC_winterspring(); + +//Zangarmarsh +extern void AddSC_zangarmarsh(); + +//Zul'Farrak +//Zul'Gurub +extern void AddSC_boss_jeklik(); +extern void AddSC_boss_venoxis(); +extern void AddSC_boss_marli(); +extern void AddSC_boss_mandokir(); +extern void AddSC_boss_gahzranka(); +extern void AddSC_boss_thekal(); +extern void AddSC_boss_arlokk(); +extern void AddSC_boss_jindo(); +extern void AddSC_boss_hakkar(); +extern void AddSC_boss_grilek(); +extern void AddSC_boss_hazzarah(); +extern void AddSC_boss_renataki(); +extern void AddSC_boss_wushoolay(); +extern void AddSC_instance_zulgurub(); +//Zul'Aman +extern void AddSC_boss_janalai(); +extern void AddSC_boss_nalorakk(); +extern void AddSC_instance_zulaman(); +extern void AddSC_zulaman(); + +// ------------------- +void LoadDatabase() +{ + //Get db string from file + char const* dbstring = NULL; + if (!SD2Config.GetString("ScriptDev2DatabaseInfo", &dbstring)) + error_log("SD2: Missing ScriptDev2 Database Info from configuration file"); + + //Initilize connection to DB + if (!dbstring || !ScriptDev2DB.Initialize(dbstring)) + error_db_log("SD2: Unable to connect to Database"); + else + { + //***Preform all DB queries here*** + QueryResult *result; + + //Get Version information + result = ScriptDev2DB.PQuery("SELECT `version`" + "FROM `sd2_db_version`"); + + if (result) + { + Field *fields = result->Fetch(); + outstring_log(" "); + outstring_log("SD2: Database version is: %s", fields[0].GetString()); + outstring_log(" "); + delete result; + + }else error_db_log("SD2: Missing sd2_db_version information."); + + // Drop existing Event AI Localized Text hash map + EventAI_LocalizedTextMap.clear(); + + // Gather EventAI Localized Texts + result = ScriptDev2DB.PQuery("SELECT `id`,`locale_1`,`locale_2`,`locale_3`,`locale_4`,`locale_5`,`locale_6`,`locale_7`,`locale_8`" + "FROM `eventai_localized_texts`"); + + if(result) + { + outstring_log("Loading EAI Localized Texts...."); + barGoLink bar(result->GetRowCount()); + uint32 count = 0; + + do + { + Localized_Text temp; + bar.step(); + + Field *fields = result->Fetch(); + + uint32 i = fields[0].GetInt32(); + + temp.locale_1 = fields[1].GetString(); + temp.locale_2 = fields[2].GetString(); + temp.locale_3 = fields[3].GetString(); + temp.locale_4 = fields[4].GetString(); + temp.locale_5 = fields[5].GetString(); + temp.locale_6 = fields[6].GetString(); + temp.locale_7 = fields[7].GetString(); + temp.locale_8 = fields[8].GetString(); + + EventAI_LocalizedTextMap[i] = temp; + ++count; + + }while(result->NextRow()); + + delete result; + + outstring_log(""); + outstring_log("SD2: Loaded %u EventAI Localized Texts", count); + }else outstring_log("SD2: WARNING >> Loaded 0 EventAI Localized Texts. Database table `eventai_localized_texts` is empty"); + + // Drop Existing Script Localized Text Hash Map + Script_LocalizedTextMap.clear(); + + // Gather Script Localized Texts + result = ScriptDev2DB.PQuery("SELECT `id`,`locale_1`,`locale_2`,`locale_3`,`locale_4`,`locale_5`,`locale_6`,`locale_7`,`locale_8`" + "FROM `script_localized_texts`"); + + if(result) + { + outstring_log("Loading Script Localized Texts...."); + barGoLink bar(result->GetRowCount()); + uint32 count = 0; + + do + { + Localized_Text temp; + bar.step(); + + Field *fields = result->Fetch(); + + uint32 i = fields[0].GetInt32(); + + temp.locale_1 = fields[1].GetString(); + temp.locale_2 = fields[2].GetString(); + temp.locale_3 = fields[3].GetString(); + temp.locale_4 = fields[4].GetString(); + temp.locale_5 = fields[5].GetString(); + temp.locale_6 = fields[6].GetString(); + temp.locale_7 = fields[7].GetString(); + temp.locale_8 = fields[8].GetString(); + + Script_LocalizedTextMap[i] = temp; + ++count; + + }while(result->NextRow()); + + delete result; + + outstring_log(""); + outstring_log("SD2: Loaded %u Script Localized Texts", count); + }else outstring_log("SD2: WARNING >> Loaded 0 Script Localized Texts. Database table `script_localized_texts` is empty"); + + //Drop existing EventAI Text hash map + EventAI_Text_Map.clear(); + + //Gather EventAI Text Entries + result = ScriptDev2DB.PQuery("SELECT `id`,`text` FROM `eventai_texts`"); + + if (result) + { + outstring_log( "SD2: Loading EventAI_Texts..."); + barGoLink bar(result->GetRowCount()); + uint32 Count = 0; + + do + { + bar.step(); + Field *fields = result->Fetch(); + + uint32 i = fields[0].GetInt32(); + + std::string text = fields[1].GetString(); + + if (!strlen(text.c_str())) + error_db_log("SD2: EventAI text %u is empty", i); + + EventAI_Text_Map[i] = text; + ++Count; + + }while (result->NextRow()); + + delete result; + + outstring_log(""); + outstring_log("SD2: >> Loaded %u EventAI_Texts", Count); + + }else outstring_log("SD2: WARNING >> Loaded 0 EventAI_Texts. DB table `EventAI_Texts` is empty."); + + //Gather event data + result = ScriptDev2DB.PQuery("SELECT `id`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`" + "FROM `eventai_summons`"); + + //Drop Existing EventSummon Map + EventAI_Summon_Map.clear(); + + if (result) + { + outstring_log( "SD2: Loading EventAI_Summons..."); + barGoLink bar(result->GetRowCount()); + uint32 Count = 0; + + do + { + bar.step(); + Field *fields = result->Fetch(); + + EventAI_Summon temp; + + uint32 i = fields[0].GetUInt32(); + temp.position_x = fields[1].GetFloat(); + temp.position_y = fields[2].GetFloat(); + temp.position_z = fields[3].GetFloat(); + temp.orientation = fields[4].GetFloat(); + temp.SpawnTimeSecs = fields[5].GetUInt32(); + + //Add to map + EventAI_Summon_Map[i] = temp; + ++Count; + + }while (result->NextRow()); + + delete result; + outstring_log(""); + outstring_log("SD2: >> Loaded %u EventAI_Summons", Count); + + }else outstring_log("SD2: WARNING >> Loaded 0 EventAI_Summons. DB table `EventAI_Summons` is empty."); + + //Gather event data + result = ScriptDev2DB.PQuery("SELECT `id`,`creature_id`,`event_type`,`event_inverse_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action1_type`,`action1_param1`,`action1_param2`,`action1_param3`,`action2_type`,`action2_param1`,`action2_param2`,`action2_param3`,`action3_type`,`action3_param1`,`action3_param2`,`action3_param3`" + "FROM `eventai_scripts`"); + + //Drop Existing EventAI List + EventAI_Event_List.clear(); + + if (result) + { + outstring_log( "SD2: Loading EventAI_Scripts..."); + barGoLink bar(result->GetRowCount()); + uint32 Count = 0; + + do + { + bar.step(); + Field *fields = result->Fetch(); + + EventAI_Event temp; + + temp.event_id = fields[0].GetUInt32(); + uint32 i = temp.event_id; + temp.creature_id = fields[1].GetUInt32(); + temp.event_type = fields[2].GetUInt16(); + temp.event_inverse_phase_mask = fields[3].GetUInt32(); + temp.event_chance = fields[4].GetUInt8(); + temp.event_flags = fields[5].GetUInt8(); + temp.event_param1 = fields[6].GetUInt32(); + temp.event_param2 = fields[7].GetUInt32(); + temp.event_param3 = fields[8].GetUInt32(); + temp.event_param4 = fields[9].GetUInt32(); + + //Report any errors in event + if (temp.event_type >= EVENT_T_END) + error_db_log("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: 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("SD2: Event %u Action %u attempts to set instance data above encounter state 3. Custom case?", i, j+1); + break; + + default: + break; + } + + if (temp.action[j].type >= ACTION_T_END) + error_db_log("SD2: Event %u Action %u has incorrect action type. Maybe DB requires updated version of SD2.", i, j+1); + } + + //Add to list + EventAI_Event_List.push_back(temp); + ++Count; + + }while (result->NextRow()); + + delete result; + outstring_log(""); + outstring_log("SD2: >> Loaded %u EventAI_Events", Count); + + }else outstring_log("SD2: WARNING >> Loaded 0 EventAI_Scripts. DB table `EventAI_Scripts` is empty."); + + // Gather Script Text + result = ScriptDev2DB.PQuery("SELECT `id`, `sound`, `type`, `language`, `text`" + "FROM `script_texts`;"); + + // Drop Existing Script Text Map + Script_TextMap.clear(); + + if(result) + { + outstring_log("SD2: Loading Script Text..."); + barGoLink bar(result->GetRowCount()); + uint32 count = 0; + + do + { + bar.step(); + Field* fields = result->Fetch(); + ScriptText temp; + + uint32 i = fields[0].GetInt32(); + temp.SoundId = fields[1].GetInt32(); + temp.Type = fields[2].GetInt32(); + temp.Language = fields[3].GetInt32(); + temp.Text = fields[4].GetString(); + + if (temp.SoundId) + { + if (!GetSoundEntriesStore()->LookupEntry(temp.SoundId)) + error_db_log("SD2: Id %u in table script_texts has soundid %u but sound does not exist.",i,temp.SoundId); + } + + if(!strlen(temp.Text.c_str())) + error_db_log("SD2: Id %u in table script_texts has no text.", i); + + Script_TextMap[i] = temp; + ++count; + + }while(result->NextRow()); + + delete result; + + outstring_log(""); + outstring_log("SD2: Loaded %u Script Texts", count); + + }else outstring_log("SD2 WARNING >> Loaded 0 Script Texts. Database table `script_texts` is empty."); + + //Free database thread and resources + ScriptDev2DB.HaltDelayThread(); + + //***End DB queries*** + } +} + +struct TSpellSummary { + uint8 Targets; // set of enum SelectTarget + uint8 Effects; // set of enum SelectEffect +}extern *SpellSummary; + +MANGOS_DLL_EXPORT +void ScriptsFree() +{ + // Free Spell Summary + delete []SpellSummary; + + // Free resources before library unload + for(int i=0;i 8) + { + Locale = 0; + error_log("SD2: Locale set to invalid language id. Defaulting to 0."); + } + + outstring_log("SD2: Using locale %u", Locale); + outstring_log(""); + + EAI_ErrorLevel = SD2Config.GetIntDefault("EAIErrorLevel", 1); + + switch (EAI_ErrorLevel) + { + case 0: + outstring_log("SD2: EventAI Error Reporting level set to 0 (Startup Errors only)"); + break; + + case 1: + outstring_log("SD2: EventAI Error Reporting level set to 1 (Startup errors and Runtime event errors)"); + break; + + case 2: + outstring_log("SD2: EventAI Error Reporting level set to 2 (Startup errors, Runtime event errors, and Creation errors)"); + break; + + default: + outstring_log("SD2: 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 SD2Config.SetSource) + LoadDatabase(); + + nrscripts = 0; + for(int i=0;i::iterator i = EventAI_LocalizedTextMap.find(entry); + + if (i == EventAI_LocalizedTextMap.end()) + { + error_log("SD2: EventAI Localized Text %u not found", entry); + return DEFAULT_TEXT; + } + + switch (Locale) + { + case 1: + temp = (*i).second.locale_1.c_str(); + break; + + case 2: + temp = (*i).second.locale_2.c_str(); + break; + + case 3: + temp = (*i).second.locale_3.c_str(); + break; + + case 4: + temp = (*i).second.locale_4.c_str(); + break; + + case 5: + temp = (*i).second.locale_5.c_str(); + break; + + case 6: + temp = (*i).second.locale_6.c_str(); + break; + + case 7: + temp = (*i).second.locale_7.c_str(); + break; + + case 8: + temp = (*i).second.locale_8.c_str(); + break; + }; + + if (strlen(temp)) + return temp; + + return DEFAULT_TEXT; +} + +const char* GetScriptLocalizedText(uint32 entry) +{ + const char* temp = NULL; + + HM_NAMESPACE::hash_map::iterator i = Script_LocalizedTextMap.find(entry); + + if (i == Script_LocalizedTextMap.end()) + { + error_log("SD2: Script Localized Text %u not found", entry); + return DEFAULT_TEXT; + } + + switch (Locale) + { + case 1: + temp = (*i).second.locale_1.c_str(); + break; + + case 2: + temp = (*i).second.locale_2.c_str(); + break; + + case 3: + temp = (*i).second.locale_3.c_str(); + break; + + case 4: + temp = (*i).second.locale_4.c_str(); + break; + + case 5: + temp = (*i).second.locale_5.c_str(); + break; + + case 6: + temp = (*i).second.locale_6.c_str(); + break; + + case 7: + temp = (*i).second.locale_7.c_str(); + break; + + case 8: + temp = (*i).second.locale_8.c_str(); + break; + }; + + if (strlen(temp)) + return temp; + + return DEFAULT_TEXT; +} + +const char* GetEventAIText(uint32 entry) +{ + if(entry == 0xffffffff) + error_log("SD2: Entry = -1, GetEventAIText should not be called in this case."); + + const char* str = NULL; + + HM_NAMESPACE::hash_map::iterator itr = EventAI_Text_Map.find(entry); + if(itr == EventAI_Text_Map.end()) + { + error_log("SD2 ERROR: Unable to find EventAI Text %u", entry); + return DEFAULT_TEXT; + } + + str = (*itr).second.c_str(); + + if(strlen(str)) + return str; + + if(strlen((*itr).second.c_str())) + return (*itr).second.c_str(); + + return DEFAULT_TEXT; +} + +void ProcessScriptText(uint32 id, WorldObject* pSource, Unit* target) +{ + if (!pSource) + { + error_log("SD2: ProcessScriptText invalid Source pointer."); + return; + } + + HM_NAMESPACE::hash_map::iterator i = Script_TextMap.find(id); + + if (i == Script_TextMap.end()) + { + error_log("SD2: ProcessScriptText could not find id %u.",id); + return; + } + + if((*i).second.SoundId) + { + if(GetSoundEntriesStore()->LookupEntry((*i).second.SoundId)) + { + WorldPacket data(4); + data.SetOpcode(SMSG_PLAY_SOUND); + data << uint32((*i).second.SoundId); + pSource->SendMessageToSet(&data,false); + } + else + error_log("SD2: ProcessScriptText id %u tried to process invalid soundid %u.",id,(*i).second.SoundId); + } + + switch((*i).second.Type) + { + case CHAT_TYPE_SAY: + pSource->MonsterSay((*i).second.Text.c_str(), (*i).second.Language, target ? target->GetGUID() : 0); + break; + + case CHAT_TYPE_YELL: + pSource->MonsterYell((*i).second.Text.c_str(), (*i).second.Language, target ? target->GetGUID() : 0); + break; + + case CHAT_TYPE_TEXT_EMOTE: + pSource->MonsterTextEmote((*i).second.Text.c_str(), target ? target->GetGUID() : 0); + break; + + case CHAT_TYPE_BOSS_EMOTE: + pSource->MonsterTextEmote((*i).second.Text.c_str(), target ? target->GetGUID() : 0, true); + break; + + case CHAT_TYPE_WHISPER: + { + if (target && target->GetTypeId() == TYPEID_PLAYER) + pSource->MonsterWhisper((*i).second.Text.c_str(), target->GetGUID()); + else error_log("SD2: ProcessScriptText id %u cannot whisper without target unit (TYPEID_PLAYER).", id); + }break; + + case CHAT_TYPE_BOSS_WHISPER: + { + if (target && target->GetTypeId() == TYPEID_PLAYER) + pSource->MonsterWhisper((*i).second.Text.c_str(), target->GetGUID(), true); + else error_log("SD2: ProcessScriptText id %u cannot whisper without target unit (TYPEID_PLAYER).", id); + }break; + } +} + +Script* GetScriptByName(std::string Name) +{ + if(Name.empty()) + return NULL; + + for(int i=0;iName == 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("SD2: 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("SD2: 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 new file mode 100644 index 00000000000..8f0f05afe3d --- /dev/null +++ b/src/bindings/scripts/ScriptMgr.h @@ -0,0 +1,94 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SCRIPTMGR_H +#define SCRIPTMGR_H + +#include "Common.h" +#include "Platform/CompilerDefs.h" +#include "Database/DBCStructure.h" + +class Player; +class Creature; +class CreatureAI; +class InstanceData; +class Quest; +class Item; +class GameObject; +class SpellCastTargets; +class Map; +class Unit; +class WorldObject; + +#define MAX_SCRIPTS 1000 //72 bytes each (approx 71kb) + +//MAX visible range (size of grid) +#define VISIBLE_RANGE (166.0f) + +#define DEFAULT_TEXT "" + +// +struct Script +{ + Script() : +pGossipHello(NULL), pQuestAccept(NULL), pGossipSelect(NULL), pGossipSelectWithCode(NULL), +pQuestSelect(NULL), pQuestComplete(NULL), pNPCDialogStatus(NULL), pGODialogStatus(NULL), pChooseReward(NULL), +pItemHello(NULL), pGOHello(NULL), pAreaTrigger(NULL), pItemQuestAccept(NULL), pGOQuestAccept(NULL), +pGOChooseReward(NULL),pReceiveEmote(NULL),pItemUse(NULL), GetAI(NULL), GetInstanceData(NULL) +{} + +std::string Name; + +// -- Quest/gossip Methods to be scripted -- +bool (*pGossipHello )(Player*, Creature*); +bool (*pQuestAccept )(Player*, Creature*, Quest const* ); +bool (*pGossipSelect )(Player*, Creature*, uint32 , uint32 ); +bool (*pGossipSelectWithCode)(Player*, Creature*, uint32 , uint32 , const char* ); +bool (*pQuestSelect )(Player*, Creature*, Quest const* ); +bool (*pQuestComplete )(Player*, Creature*, Quest const* ); +uint32 (*pNPCDialogStatus )(Player*, Creature* ); +uint32 (*pGODialogStatus )(Player *player, GameObject * _GO ); +bool (*pChooseReward )(Player*, Creature*, Quest const*, uint32 ); +bool (*pItemHello )(Player*, Item*, Quest const* ); +bool (*pGOHello )(Player*, GameObject* ); +bool (*pAreaTrigger )(Player*, AreaTriggerEntry* ); +bool (*pItemQuestAccept )(Player*, Item *, Quest const* ); +bool (*pGOQuestAccept )(Player*, GameObject*, Quest const* ); +bool (*pGOChooseReward )(Player*, GameObject*_GO, Quest const*, uint32 ); +bool (*pReceiveEmote )(Player*, Creature*, uint32 ); +bool (*pItemUse )(Player*, Item*, SpellCastTargets const& ); + +CreatureAI* (*GetAI)(Creature*); +InstanceData* (*GetInstanceData)(Map*); +// ----------------------------------------- +}; + +extern int nrscripts; +extern Script *m_scripts[MAX_SCRIPTS]; + +// Localized Text function +const char* GetEventAILocalizedText(uint32 entry); +const char* GetScriptLocalizedText(uint32 entry); + +//EventAI text function +const char* GetEventAIText(uint32 entry); // TODO: Locales + +// Script Text function +void ProcessScriptText(uint32 id, WorldObject* pSource, Unit* target = NULL); // TODO: Locales + +#if COMPILER == COMPILER_GNU +#define FUNC_PTR(name,callconvention,returntype,parameters) typedef returntype(*name)parameters __attribute__ ((callconvention)); +#else +#define FUNC_PTR(name, callconvention, returntype, parameters) typedef returntype(callconvention *name)parameters; +#endif + +#ifdef WIN32 + #define MANGOS_DLL_EXPORT extern "C" __declspec(dllexport) +#elif defined( __GNUC__ ) + #define MANGOS_DLL_EXPORT extern "C" +#else + #define MANGOS_DLL_EXPORT extern "C" export +#endif + +#endif diff --git a/src/bindings/scripts/VC71/71ScriptDev2.vcproj b/src/bindings/scripts/VC71/71ScriptDev2.vcproj new file mode 100644 index 00000000000..4b015248add --- /dev/null +++ b/src/bindings/scripts/VC71/71ScriptDev2.vcproj @@ -0,0 +1,1598 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/bindings/scripts/VC80/80ScriptDev2.vcproj b/src/bindings/scripts/VC80/80ScriptDev2.vcproj new file mode 100644 index 00000000000..c546b189e28 --- /dev/null +++ b/src/bindings/scripts/VC80/80ScriptDev2.vcproj @@ -0,0 +1,2347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/bindings/scripts/VC90/90ScriptDev2.vcproj b/src/bindings/scripts/VC90/90ScriptDev2.vcproj new file mode 100644 index 00000000000..6a08eee143b --- /dev/null +++ b/src/bindings/scripts/VC90/90ScriptDev2.vcproj @@ -0,0 +1,2345 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/bindings/scripts/VCProjToLinuxMake.exe b/src/bindings/scripts/VCProjToLinuxMake.exe new file mode 100644 index 00000000000..1862fe32e3e Binary files /dev/null and b/src/bindings/scripts/VCProjToLinuxMake.exe differ diff --git a/src/bindings/scripts/docs/EventAI.txt b/src/bindings/scripts/docs/EventAI.txt new file mode 100644 index 00000000000..af4335655b8 --- /dev/null +++ b/src/bindings/scripts/docs/EventAI.txt @@ -0,0 +1,701 @@ +========================================= +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_.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 + +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/docs/How to install.txt b/src/bindings/scripts/docs/How to install.txt new file mode 100644 index 00000000000..10efdf94de9 --- /dev/null +++ b/src/bindings/scripts/docs/How to install.txt @@ -0,0 +1,32 @@ +--- How to install ScriptDev2 --- + +1) Download MaNGOS +2) Create a new folder under "src\bindings\" within the MaNGOS source called "ScriptDev2" +3) Checkout the ScriptDev2 trunk from "https://scriptdev2.svn.sourceforge.net/svnroot/scriptdev2" + +4a) On Win32: + - Compile MaNGOS + - Compile ScriptDev2 using the ScriptVC70, ScriptVC80, or ScriptVC90 Solution within the ScriptDev2 folder (this will overwrite the Mangoscript dll in the output directory) + +4b) On Linux: +- Apply MaNGOS-rXXXX-ScriptDev2.patch, where XXXX is the highest version to that of your MaNGOS revision, to Mangos Source +- Compile MaNGOS (ScriptDev2 will automatically be built when compiling Mangos from here on) + +5) Create the default ScriptDev2 database using "sql\created_database.sql", then execute "sql\scriptdev2_structure.sql" on that database. + +6) Execute the included "sql\mangos_full_scripts.sql" on your MaNGOS database. + +7) Place the included "scriptdev2.conf" file within the directory containing your "mangosd.conf" and "realmd.conf" files. You may need to change this file to match the database you created and any custom settings you wish to use (such as a different locale). + +8) Run mangosd from your output directory + + +To update ScriptDev2: +All you have to do is open src\bindings\ and right click on the ScriptDev2 folder and click "Update" and then follow steps 4, 6, 7, and 8 again. You must still compile MaNGOS before ScriptDev2 when on the Windows platform. + +To update your Database with new Scriptdev2 SQL changes you can either: +a) apply only the changes that were made during that revision by looking in the sql\update folder or (files named rXXX_scriptdev2.sql should be executed on the scriptdev2 db while rXXX_mangos.sql should be executed on your mangos db) +b) reapply "mangos_full_scripts.sql" to your MaNGOS database. + +You can view the ScriptDev2 Change Log at: +[url=http://scriptdev2.svn.sourceforge.net/viewvc/scriptdev2/?view=log]http://scriptdev2.svn.sourceforge.net/viewvc/scriptdev2/?view=log[/url] \ No newline at end of file diff --git a/src/bindings/scripts/docs/LICENSE.txt b/src/bindings/scripts/docs/LICENSE.txt new file mode 100644 index 00000000000..69cd8a1df6d --- /dev/null +++ b/src/bindings/scripts/docs/LICENSE.txt @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS \ No newline at end of file diff --git a/src/bindings/scripts/docs/Script Layout.txt b/src/bindings/scripts/docs/Script Layout.txt new file mode 100644 index 00000000000..2c749952f70 --- /dev/null +++ b/src/bindings/scripts/docs/Script Layout.txt @@ -0,0 +1,32 @@ +--- Script Layout --- +A quick explination of the layout I hope everyone will follow for scriptdev2. + +--- Sub Folders --- + +Area - Contains scripts used solely by area triggers + +Boss - Boss scripts for bosses that are not zone specific + +Mob - Generic Creature scripts for creatures that are not zone specific + +Custom - Intentionally empty folder from SVN. If you make a custom script please put it here. + +GO - Contains scripts used solely by Game Objects (GOs) that do not have a specific zone + +Guard - Scripts for Guard NPCs + +Honor - Honor npcs (currently a blank script as these npcs do nothing special) + +Item - Item scripts + +NPC - Scripts for individual NPCs who do not have a specific zone + +Servers - Generic NPC servers script for things such as flightmasters and guildmasters. + +Zone - ALL zone specific scripts should be written within these folders. This includes creature scripts, boss scripts, go scripts, area scripts, and npc scripts. + +--- Naming Conventions --- + +Please keep file names to "type_objectname.cpp" where type is replaced by the type of object and objectname is replaced by the name of the object, creature, item, or area that this script will be used by. + +AddSC functions should follow "void AddSC_creaturename(void);" format. Do not append AI or anything else. \ No newline at end of file diff --git a/src/bindings/scripts/docs/ToDo.txt b/src/bindings/scripts/docs/ToDo.txt new file mode 100644 index 00000000000..5df315da037 --- /dev/null +++ b/src/bindings/scripts/docs/ToDo.txt @@ -0,0 +1,14 @@ +--- TO DO --- +Simple list of things we need to do. + +--- ScriptDev2 Framework --- +Move all text out of C++ code and into SD2 database. + +--- Scripting with Priorities --- +1) Boss AI and Boss Event scripts +2) Specific NPC AI, Gossip, Quest, Event scripts +3) Spell and Item scripts + +--- Core Problems --- + +- Spell scripts are only scriptable within the core. Spell scripts should be done in SD2 since they are very specialized. \ No newline at end of file diff --git a/src/bindings/scripts/include/precompiled.cpp b/src/bindings/scripts/include/precompiled.cpp new file mode 100644 index 00000000000..8863d83ac06 --- /dev/null +++ b/src/bindings/scripts/include/precompiled.cpp @@ -0,0 +1,5 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/include/precompiled.h b/src/bindings/scripts/include/precompiled.h new file mode 100644 index 00000000000..dda4d672f38 --- /dev/null +++ b/src/bindings/scripts/include/precompiled.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_PRECOMPILED_H +#define SC_PRECOMPILED_H + +#include "../ScriptMgr.h" +#include "sc_creature.h" +#include "sc_gossip.h" +#include "sc_grid_searchers.h" +#include "sc_instance.h" + +#ifdef WIN32 +#include +BOOL APIENTRY DllMain( HANDLE hModule, +DWORD ul_reason_for_call, +LPVOID lpReserved +) +{ + return true; +} +#endif + +#endif diff --git a/src/bindings/scripts/include/sc_creature.cpp b/src/bindings/scripts/include/sc_creature.cpp new file mode 100644 index 00000000000..afdbf590c65 --- /dev/null +++ b/src/bindings/scripts/include/sc_creature.cpp @@ -0,0 +1,612 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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" +#include "WorldPacket.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->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) + { + Aggro(who); + InCombat = true; + } + } + } +} + +void ScriptedAI::AttackStart(Unit* who) +{ + if (!who) + return; + + if (who->isTargetableForAttack()) + { + //Begin attack + DoStartAttackAndMovement(who); + + if (!InCombat) + { + Aggro(who); + InCombat = true; + } + } +} + +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; + } + + WorldPacket data(4); + data.SetOpcode(SMSG_PLAY_SOUND); + data << uint32(sound); + unit->SendMessageToSet(&data,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& m_threatlist = m_creature->getThreatManager().getThreatList(); + std::list::iterator i = m_threatlist.begin(); + std::list::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& m_threatlist = m_creature->getThreatManager().getThreatList(); + std::list::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; + + MostHPMissingInRange u_check(m_creature, range, MinHPDiff); + MaNGOS::UnitLastSearcher 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, GridTypeMapContainer > grid_unit_searcher(searcher); + + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_unit_searcher, *(m_creature->GetMap())); + return pUnit; +} + +std::list 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 pList; + + FriendlyCCedInRange u_check(m_creature, range); + MaNGOS::CreatureListSearcher searcher(pList, u_check); + + TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); + + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_creature_searcher, *(m_creature->GetMap())); + + return pList; +} + +std::list 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 pList; + + FriendlyMissingBuffInRange u_check(m_creature, range, spellid); + MaNGOS::CreatureListSearcher searcher(pList, u_check); + + TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); + + CellLock 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->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) + { + Aggro(who); + InCombat = true; + } + } + } +} + +void Scripted_NoMovementAI::AttackStart(Unit* who) +{ + if (!who) + return; + + if (who->isTargetableForAttack()) + { + //Begin attack + DoStartAttackNoMovement(who); + + if (!InCombat) + { + Aggro(who); + InCombat = true; + } + } +} diff --git a/src/bindings/scripts/include/sc_creature.h b/src/bindings/scripts/include/sc_creature.h new file mode 100644 index 00000000000..13d57a6e78b --- /dev/null +++ b/src/bindings/scripts/include/sc_creature.h @@ -0,0 +1,196 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_CREATURE_H +#define SC_CREATURE_H + +#include "CreatureAI.h" +#include "Creature.h" + +//Spell targets used by SelectSpell +enum SelectTarget +{ + SELECT_TARGET_DONTCARE = 0, //All target types allowed + + SELECT_TARGET_SELF, //Only Self casting + + SELECT_TARGET_SINGLE_ENEMY, //Only Single Enemy + SELECT_TARGET_AOE_ENEMY, //Only AoE Enemy + SELECT_TARGET_ANY_ENEMY, //AoE or Single Enemy + + SELECT_TARGET_SINGLE_FRIEND, //Only Single Friend + SELECT_TARGET_AOE_FRIEND, //Only AoE Friend + SELECT_TARGET_ANY_FRIEND, //AoE or Single Friend +}; + +//Spell Effects used by SelectSpell +enum SelectEffect +{ + SELECT_EFFECT_DONTCARE = 0, //All spell effects allowed + SELECT_EFFECT_DAMAGE, //Spell does damage + SELECT_EFFECT_HEALING, //Spell does healing + SELECT_EFFECT_AURA, //Spell applies an aura +}; + +//Selection method used by SelectTarget +enum SelectAggroTarget +{ + SELECT_TARGET_RANDOM = 0, //Just selects a random target + SELECT_TARGET_TOPAGGRO, //Selects targes from top aggro to bottom + SELECT_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top +}; + +struct MANGOS_DLL_DECL ScriptedAI : public CreatureAI +{ + ScriptedAI(Creature* creature) : m_creature(creature), InCombat(false) {} + ~ScriptedAI() {} + + //************* + //CreatureAI Functions + //************* + + //Called if IsVisible(Unit *who) is true at each *who move + void MoveInLineOfSight(Unit *); + + //Called at each attack of m_creature by any victim + void AttackStart(Unit *); + + //Called at stoping attack by any attacker + void EnterEvadeMode(); + + //Called at any heal cast/item used (call non implemented in mangos) + void HealBy(Unit *healer, uint32 amount_healed) {} + + // Called at any Damage to any victim (before damage apply) + void DamageDeal(Unit *done_to, uint32 &damage) {} + + // Called at any Damage from any attacker (before damage apply) + void DamageTaken(Unit *done_by, uint32 &damage) {} + + //Is unit visible for MoveInLineOfSight + bool IsVisible(Unit *who) const; + + //Called at World update tick + void UpdateAI(const uint32); + + //Called at creature death + void JustDied(Unit*){} + + //Called at creature killing another unit + void KilledUnit(Unit*){} + + // Called when the creature summon successfully other creature + void JustSummoned(Creature* ) {} + + // Called when a summoned creature is despawned + void SummonedCreatureDespawn(Creature* /*unit*/) {} + + // Called when hit by a spell + void SpellHit(Unit*, const SpellEntry*) {} + + // Called when creature is spawned or respawned (for reseting variables) + void JustRespawned(); + + //Called at waypoint reached or PointMovement end + void MovementInform(uint32, uint32){} + + //************* + // Variables + //************* + + //Pointer to creature we are manipulating + Creature* m_creature; + + //Bool for if we are in combat or not + bool InCombat; + + //************* + //Pure virtual functions + //************* + + //Called at creature reset either by death or evade + virtual void Reset() = 0; + + //Called at creature aggro either by MoveInLOS or Attack Start + virtual void Aggro(Unit*) = 0; + + //************* + //AI Helper Functions + //************* + + //Start attack of victim and go to him + void DoStartAttackAndMovement(Unit* victim, float distance = 0, float angle = 0); + + //Start attack on victim but do not move + void DoStartAttackNoMovement(Unit* victim); + + //Do melee swing of current victim if in rnage and ready and not casting + void DoMeleeAttackIfReady(); + + //Stop attack of current victim + void DoStopAttack(); + + //Cast spell by Id + void DoCast(Unit* victim, uint32 spellId, bool triggered = false); + + //Cast spell by spell info + void DoCastSpell(Unit* who,SpellEntry const *spellInfo, bool triggered = false); + + //Creature say + void DoSay(const char* text, uint32 language, Unit* target); + + //Creature Yell + void DoYell(const char* text, uint32 language, Unit* target); + + //Creature Text emote, optional bool for boss emote text + void DoTextEmote(const char* text, Unit* target, bool IsBossEmote = false); + + //Creature whisper, optional bool for boss whisper + void DoWhisper(const char* text, Unit* reciever, bool IsBossWhisper = false); + + //Plays a sound to all nearby players + void DoPlaySoundToSet(Unit* unit, uint32 sound); + + //Places the entire map into combat with creature + void DoZoneInCombat(Unit* pUnit = 0); + + //Drops all threat to 0%. Does not remove players from the threat list + void DoResetThreat(); + + //Teleports a player without dropping threat (only teleports to same map) + void DoTeleportPlayer(Unit* pUnit, float x, float y, float z, float o); + + //Returns friendly unit with the most amount of hp missing from max hp + Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff = 1); + + //Returns a list of friendly CC'd units within range + std::list DoFindFriendlyCC(float range); + + //Returns a list of all friendly units missing a specific buff within range + std::list DoFindFriendlyMissingBuff(float range, uint32 spellid); + + //Spawns a creature relative to m_creature + Creature* DoSpawnCreature(uint32 id, float x, float y, float z, float angle, uint32 type, uint32 despawntime); + + //Selects a unit from the creature's current aggro list + Unit* SelectUnit(SelectAggroTarget target, uint32 position); + + //Returns spells that meet the specified criteria from the creatures spell list + SpellEntry const* SelectSpell(Unit* Target, int32 School, int32 Mechanic, SelectTarget Targets, uint32 PowerCostMin, uint32 PowerCostMax, float RangeMin, float RangeMax, SelectEffect Effect); + + //Checks if you can cast the specified spell + bool CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered = false); +}; + +struct MANGOS_DLL_DECL Scripted_NoMovementAI : public ScriptedAI +{ + Scripted_NoMovementAI(Creature* creature) : ScriptedAI(creature) {} + + //Called if IsVisible(Unit *who) is true at each *who move + void MoveInLineOfSight(Unit *); + + //Called at each attack of m_creature by any victim + void AttackStart(Unit *); +}; +#endif diff --git a/src/bindings/scripts/include/sc_gossip.h b/src/bindings/scripts/include/sc_gossip.h new file mode 100644 index 00000000000..af304a2d63f --- /dev/null +++ b/src/bindings/scripts/include/sc_gossip.h @@ -0,0 +1,183 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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) PlayerTalkClass->GetGossipMenu()->AddMenuItem(a,b,c,d,e,f) + +// 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/include/sc_grid_searchers.h b/src/bindings/scripts/include/sc_grid_searchers.h new file mode 100644 index 00000000000..750f1d42fbe --- /dev/null +++ b/src/bindings/scripts/include/sc_grid_searchers.h @@ -0,0 +1,131 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_GRIDSEARCH_H +#define SC_GRIDSEARCH_H + +#include "Unit.h" +#include "GameObject.h" + +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" + +//Used in: +//sc_creature.cpp - DoSelectLowestHpFriendly() +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; +}; + +//Used in: +//sc_creature.cpp - DoFindFriendlyCC() +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_STUNDED) || u->hasUnitState(UNIT_STAT_STUNDED) || u->hasUnitState(UNIT_STAT_CONFUSED))) + { + return true; + } + return false; + } +private: + Unit const* i_obj; + float i_range; +}; + +//Used in: +//sc_creature.cpp - DoFindFriendlyMissingBuff() +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; +}; + +//Used in: +//hyjalAI.cpp +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; +}; + +//Used in: +//hyjalAI.cpp +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; +}; + +#endif diff --git a/src/bindings/scripts/include/sc_instance.h b/src/bindings/scripts/include/sc_instance.h new file mode 100644 index 00000000000..f16590f2a83 --- /dev/null +++ b/src/bindings/scripts/include/sc_instance.h @@ -0,0 +1,48 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_INSTANCE_H +#define SC_INSTANCE_H + +#include "InstanceData.h" +#include "Map.h" + +enum EncounterState +{ + NOT_STARTED = 0, + IN_PROGRESS = 1, + FAIL = 2, + DONE = 3 +}; + +#define OUT_SAVE_INST_DATA debug_log("SD2: Saving Instance Data for Instance %s (Map %d, Instance Id %d)", instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) +#define OUT_SAVE_INST_DATA_COMPLETE debug_log("SD2: Saving Instance Data for Instance %s (Map %d, Instance Id %d) completed.", instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) +#define OUT_LOAD_INST_DATA(a) debug_log("SD2: Loading Instance Data for Instance %s (Map %d, Instance Id %d). Input is '%s'", instance->GetMapName(), instance->GetId(), instance->GetInstanceId(), a) +#define OUT_LOAD_INST_DATA_COMPLETE debug_log("SD2: Instance Data Load for Instance %s (Map %d, Instance Id: %d) is complete.",instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) +#define OUT_LOAD_INST_DATA_FAIL error_log("SD2: Unable to load Instance Data for Instance %s (Map %d, Instance Id: %d).",instance->GetMapName(), instance->GetId(), instance->GetInstanceId()) + +class MANGOS_DLL_DECL ScriptedInstance : public InstanceData +{ + public: + + ScriptedInstance(Map *map) : InstanceData(map) {} + ~ScriptedInstance() {} + + //All-purpose data storage 64 bit + virtual uint64 GetData64(uint32) {return 0; } + virtual void SetData64(uint32, uint64) { } + + //All-purpose data storage 32 bit + virtual uint32 GetData(uint32) { return 0; } + virtual void SetData(uint32, uint32 data) {} + + // Called every instance update + virtual void Update(uint32) {} + + // Save and Load instance data to the database + const char* Save() { return NULL; } + void Load(const char* in) { } +}; + +#endif diff --git a/src/bindings/scripts/patches/MaNGOS-r4106-ScriptDev2.patch b/src/bindings/scripts/patches/MaNGOS-r4106-ScriptDev2.patch new file mode 100644 index 00000000000..a4165b87524 --- /dev/null +++ b/src/bindings/scripts/patches/MaNGOS-r4106-ScriptDev2.patch @@ -0,0 +1,40 @@ +Index: configure.ac +=================================================================== +--- configure.ac (revision 4106) ++++ configure.ac (working copy) +@@ -192,7 +192,9 @@ + src/mangosd/Makefile + src/mangosd/mangosd.conf + src/bindings/Makefile +- src/bindings/universal/Makefile ++ src/bindings/ScriptDev2/Makefile ++ src/bindings/ScriptDev2/sql/Makefile ++ src/bindings/ScriptDev2/sql/Updates/Makefile + ]) + + ## Disabled Makefiles, until they are ready for a successful make and +Index: src/bindings/Makefile.am +=================================================================== +--- src/bindings/Makefile.am (revision 4106) ++++ src/bindings/Makefile.am (working copy) +@@ -14,4 +14,4 @@ + # along with this program; if not, write to the Free Software + # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +-SUBDIRS = universal ++SUBDIRS = ScriptDev2 +Index: src/mangosd/Makefile.am +=================================================================== +--- src/mangosd/Makefile.am (revision 4106) ++++ src/mangosd/Makefile.am (working copy) +@@ -33,8 +33,8 @@ + WorldRunnable.h + + ## Link world daemon against the shared library +-mangos_worldd_LDADD = ../bindings/universal/libmangosscript.la ../game/libmangosgame.a ../shared/libmangosdatabase.a ../shared/libmangosconfig.a ../shared/libmangosauth.a ../shared/libmangosshared.a ../shared/libmangosvmaps.a ../shared/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a +-mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/universal/ $(MYSQL_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic ++mangos_worldd_LDADD = ../bindings/ScriptDev2/libmangosscript.la ../game/libmangosgame.a ../shared/libmangosdatabase.a ../shared/libmangosconfig.a ../shared/libmangosauth.a ../shared/libmangosshared.a ../shared/libmangosvmaps.a ../shared/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a ++mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/ScriptDev2/ $(MYSQL_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic + + ## Additional files to include when running 'make dist' + # Include world daemon configuration diff --git a/src/bindings/scripts/patches/MaNGOS-r4203-ScriptDev2.patch b/src/bindings/scripts/patches/MaNGOS-r4203-ScriptDev2.patch new file mode 100644 index 00000000000..383607f1ad1 --- /dev/null +++ b/src/bindings/scripts/patches/MaNGOS-r4203-ScriptDev2.patch @@ -0,0 +1,40 @@ +Index: configure.ac +=================================================================== +--- configure.ac (revision 4241) ++++ configure.ac (working copy) +@@ -223,7 +223,9 @@ + src/mangosd/Makefile + src/mangosd/mangosd.conf + src/bindings/Makefile +- src/bindings/universal/Makefile ++ src/bindings/ScriptDev2/Makefile ++ src/bindings/ScriptDev2/sql/Makefile ++ src/bindings/ScriptDev2/sql/Updates/Makefile + ]) + + ## Disabled Makefiles, until they are ready for a successful make and +Index: src/bindings/Makefile.am +=================================================================== +--- src/bindings/Makefile.am (revision 4241) ++++ src/bindings/Makefile.am (working copy) +@@ -14,4 +14,4 @@ + # along with this program; if not, write to the Free Software + # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +-SUBDIRS = universal ++SUBDIRS = ScriptDev2 +Index: src/mangosd/Makefile.am +=================================================================== +--- src/mangosd/Makefile.am (revision 4241) ++++ src/mangosd/Makefile.am (working copy) +@@ -33,8 +33,8 @@ + WorldRunnable.h + + ## Link world daemon against the shared library +-mangos_worldd_LDADD = ../bindings/universal/libmangosscript.la ../game/libmangosgame.a ../shared/libmangosdatabase.a ../shared/libmangosconfig.a ../shared/libmangosauth.a ../shared/libmangosshared.a ../shared/libmangosvmaps.a ../shared/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a +-mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/universal/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic ++mangos_worldd_LDADD = ../bindings/ScriptDev2/libmangosscript.la ../game/libmangosgame.a ../shared/libmangosdatabase.a ../shared/libmangosconfig.a ../shared/libmangosauth.a ../shared/libmangosshared.a ../shared/libmangosvmaps.a ../shared/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a ++mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/ScriptDev2/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic + + ## Additional files to include when running 'make dist' + # Include world daemon configuration diff --git a/src/bindings/scripts/patches/MaNGOS-r4241-ScriptDev2.patch b/src/bindings/scripts/patches/MaNGOS-r4241-ScriptDev2.patch new file mode 100644 index 00000000000..383607f1ad1 --- /dev/null +++ b/src/bindings/scripts/patches/MaNGOS-r4241-ScriptDev2.patch @@ -0,0 +1,40 @@ +Index: configure.ac +=================================================================== +--- configure.ac (revision 4241) ++++ configure.ac (working copy) +@@ -223,7 +223,9 @@ + src/mangosd/Makefile + src/mangosd/mangosd.conf + src/bindings/Makefile +- src/bindings/universal/Makefile ++ src/bindings/ScriptDev2/Makefile ++ src/bindings/ScriptDev2/sql/Makefile ++ src/bindings/ScriptDev2/sql/Updates/Makefile + ]) + + ## Disabled Makefiles, until they are ready for a successful make and +Index: src/bindings/Makefile.am +=================================================================== +--- src/bindings/Makefile.am (revision 4241) ++++ src/bindings/Makefile.am (working copy) +@@ -14,4 +14,4 @@ + # along with this program; if not, write to the Free Software + # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +-SUBDIRS = universal ++SUBDIRS = ScriptDev2 +Index: src/mangosd/Makefile.am +=================================================================== +--- src/mangosd/Makefile.am (revision 4241) ++++ src/mangosd/Makefile.am (working copy) +@@ -33,8 +33,8 @@ + WorldRunnable.h + + ## Link world daemon against the shared library +-mangos_worldd_LDADD = ../bindings/universal/libmangosscript.la ../game/libmangosgame.a ../shared/libmangosdatabase.a ../shared/libmangosconfig.a ../shared/libmangosauth.a ../shared/libmangosshared.a ../shared/libmangosvmaps.a ../shared/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a +-mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/universal/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic ++mangos_worldd_LDADD = ../bindings/ScriptDev2/libmangosscript.la ../game/libmangosgame.a ../shared/libmangosdatabase.a ../shared/libmangosconfig.a ../shared/libmangosauth.a ../shared/libmangosshared.a ../shared/libmangosvmaps.a ../shared/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a ++mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/ScriptDev2/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic + + ## Additional files to include when running 'make dist' + # Include world daemon configuration diff --git a/src/bindings/scripts/patches/MaNGOS-r4798-ScriptDev2.patch b/src/bindings/scripts/patches/MaNGOS-r4798-ScriptDev2.patch new file mode 100644 index 00000000000..cb08bdc8222 --- /dev/null +++ b/src/bindings/scripts/patches/MaNGOS-r4798-ScriptDev2.patch @@ -0,0 +1,40 @@ +Index: src/mangosd/Makefile.am +=================================================================== +--- src/mangosd/Makefile.am (revision 4799) ++++ src/mangosd/Makefile.am (working copy) +@@ -33,8 +33,8 @@ + WorldRunnable.h + + ## Link world daemon against the shared library +-mangos_worldd_LDADD = ../bindings/universal/libmangosscript.la ../game/libmangosgame.a ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/vmap/libmangosvmaps.a ../shared/Network/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a +-mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/universal/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic ++mangos_worldd_LDADD = ../bindings/ScriptDev2/libmangosscript.la ../game/libmangosgame.a ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/vmap/libmangosvmaps.a ../shared/Network/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a ++mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/ScriptDev2/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic + + ## Additional files to include when running 'make dist' + # Include world daemon configuration +Index: src/bindings/Makefile.am +=================================================================== +--- src/bindings/Makefile.am (revision 4799) ++++ src/bindings/Makefile.am (working copy) +@@ -14,4 +14,4 @@ + # along with this program; if not, write to the Free Software + # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +-SUBDIRS = universal ++SUBDIRS = ScriptDev2 +Index: configure.ac +=================================================================== +--- configure.ac (revision 4799) ++++ configure.ac (working copy) +@@ -228,7 +228,9 @@ + src/mangosd/Makefile + src/mangosd/mangosd.conf + src/bindings/Makefile +- src/bindings/universal/Makefile ++ src/bindings/ScriptDev2/Makefile ++ src/bindings/ScriptDev2/sql/Makefile ++ src/bindings/ScriptDev2/sql/Updates/Makefile + ]) + + ## Disabled Makefiles, until they are ready for a successful make and diff --git a/src/bindings/scripts/patches/MaNGOS-r5049-Scriptdev2.patch b/src/bindings/scripts/patches/MaNGOS-r5049-Scriptdev2.patch new file mode 100644 index 00000000000..6602ea80cb3 --- /dev/null +++ b/src/bindings/scripts/patches/MaNGOS-r5049-Scriptdev2.patch @@ -0,0 +1,42 @@ + +Index: src/mangosd/Makefile.am +=================================================================== +--- src/mangosd/Makefile.am (revision 5049) ++++ src/mangosd/Makefile.am (working copy) +@@ -33,8 +33,8 @@ + WorldRunnable.h + + ## Link world daemon against the shared library +-mangos_worldd_LDADD = ../bindings/universal/libmangosscript.la ../game/libmangosgame.a ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/vmap/libmangosvmaps.a ../framework/libmangosframework.a ../../dep/src/sockets/libmangossockets.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a +-mangos_worldd_LDFLAGS = -L../../dep/src/sockets -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/universal/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic ++mangos_worldd_LDADD = ../bindings/ScriptDev2/libmangosscript.la ../game/libmangosgame.a ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/vmap/libmangosvmaps.a ../framework/libmangosframework.a ../../dep/src/sockets/libmangossockets.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a ++mangos_worldd_LDFLAGS = -L../../dep/src/sockets -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/ScriptDev2/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic + + ## Additional files to include when running 'make dist' + # Include world daemon configuration +Index: src/bindings/Makefile.am +=================================================================== +--- src/bindings/Makefile.am (revision 5049) ++++ src/bindings/Makefile.am (working copy) +@@ -14,4 +14,4 @@ + # along with this program; if not, write to the Free Software + # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +-SUBDIRS = universal ++SUBDIRS = ScriptDev2 +Index: configure.ac +=================================================================== +--- configure.ac (revision 5049) ++++ configure.ac (working copy) +@@ -228,7 +228,10 @@ + src/mangosd/Makefile + src/mangosd/mangosd.conf + src/bindings/Makefile +- src/bindings/universal/Makefile ++ src/bindings/ScriptDev2/Makefile ++ src/bindings/ScriptDev2/config.h ++ src/bindings/ScriptDev2/sql/Makefile ++ src/bindings/ScriptDev2/sql/Updates/Makefile + ]) + + ## Disabled Makefiles, until they are ready for a successful make and diff --git a/src/bindings/scripts/patches/MaNGOS-r6491-Scriptdev2.patch b/src/bindings/scripts/patches/MaNGOS-r6491-Scriptdev2.patch new file mode 100644 index 00000000000..2e98e250b91 --- /dev/null +++ b/src/bindings/scripts/patches/MaNGOS-r6491-Scriptdev2.patch @@ -0,0 +1,41 @@ +Index: configure.ac +=================================================================== +--- configure.ac (revision 6491) ++++ configure.ac (working copy) +@@ -238,7 +238,10 @@ + src/mangosd/Makefile + src/mangosd/mangosd.conf + src/bindings/Makefile +- src/bindings/universal/Makefile ++ src/bindings/ScriptDev2/Makefile ++ src/bindings/ScriptDev2/config.h ++ src/bindings/ScriptDev2/sql/Makefile ++ src/bindings/ScriptDev2/sql/Updates/Makefile + ]) + + ## Disabled Makefiles, until they are ready for a successful make and +Index: src/bindings/Makefile.am +=================================================================== +--- src/bindings/Makefile.am (revision 6491) ++++ src/bindings/Makefile.am (working copy) +@@ -14,4 +14,4 @@ + # along with this program; if not, write to the Free Software + # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +-SUBDIRS = universal ++SUBDIRS = ScriptDev2 +Index: src/mangosd/Makefile.am +=================================================================== +--- src/mangosd/Makefile.am (revision 6491) ++++ src/mangosd/Makefile.am (working copy) +@@ -33,8 +33,8 @@ + WorldRunnable.h + + ## Link world daemon against the shared library +-mangos_worldd_LDADD = ../bindings/universal/libmangosscript.la ../game/libmangosgame.a ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/vmap/libmangosvmaps.a ../framework/libmangosframework.a ../../dep/src/sockets/libmangossockets.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a +-mangos_worldd_LDFLAGS = -L../../dep/src/sockets -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/universal/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic ++mangos_worldd_LDADD = ../bindings/ScriptDev2/libmangosscript.la ../game/libmangosgame.a ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/vmap/libmangosvmaps.a ../framework/libmangosframework.a ../../dep/src/sockets/libmangossockets.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a ++mangos_worldd_LDFLAGS = -L../../dep/src/sockets -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/ScriptDev2/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic + + ## Additional files to include when running 'make dist' + # Include world daemon configuration diff --git a/src/bindings/scripts/patches/MaNGOS-r6657-Scriptdev2.patch b/src/bindings/scripts/patches/MaNGOS-r6657-Scriptdev2.patch new file mode 100644 index 00000000000..8cd5cc63c0f --- /dev/null +++ b/src/bindings/scripts/patches/MaNGOS-r6657-Scriptdev2.patch @@ -0,0 +1,42 @@ +Index: src/mangosd/Makefile.am +=================================================================== +--- src/mangosd/Makefile.am (revision 6657) ++++ src/mangosd/Makefile.am (working copy) +@@ -33,8 +33,8 @@ + WorldRunnable.h + + ## Link world daemon against the shared library +-mangos_worldd_LDADD = ../bindings/universal/libmangosscript.la ../game/libmangosgame.a ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/vmap/libmangosvmaps.a ../framework/libmangosframework.a ../../dep/src/sockets/libmangossockets.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a +-mangos_worldd_LDFLAGS = -L../../dep/src/sockets -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/universal/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic ++mangos_worldd_LDADD = ../bindings/ScriptDev2/libmangosscript.la ../game/libmangosgame.a ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/vmap/libmangosvmaps.a ../framework/libmangosframework.a ../../dep/src/sockets/libmangossockets.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a ++mangos_worldd_LDFLAGS = -L../../dep/src/sockets -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/ScriptDev2/ $(MYSQL_LIBS) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic + + ## Additional files to include when running 'make dist' + # Include world daemon configuration +Index: src/bindings/Makefile.am +=================================================================== +--- src/bindings/Makefile.am (revision 6656) ++++ src/bindings/Makefile.am (working copy) +@@ -14,4 +14,4 @@ + # along with this program; if not, write to the Free Software + # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +-SUBDIRS = universal ++SUBDIRS = ScriptDev2 +Index: configure.ac +=================================================================== +--- configure.ac (revision 6657) ++++ configure.ac (working copy) +@@ -238,7 +238,11 @@ + src/mangosd/Makefile + src/mangosd/mangosd.conf.dist + src/bindings/Makefile +- src/bindings/universal/Makefile ++ src/bindings/ScriptDev2/Makefile ++ src/bindings/ScriptDev2/scriptdev2.conf.dist ++ src/bindings/ScriptDev2/config.h ++ src/bindings/ScriptDev2/sql/Makefile ++ src/bindings/ScriptDev2/sql/Updates/Makefile + ]) + + ## Disabled Makefiles, until they are ready for a successful make and diff --git a/src/bindings/scripts/scripts/areatrigger/areatrigger_scripts.cpp b/src/bindings/scripts/scripts/areatrigger/areatrigger_scripts.cpp new file mode 100644 index 00000000000..c62d7c33bed --- /dev/null +++ b/src/bindings/scripts/scripts/areatrigger/areatrigger_scripts.cpp @@ -0,0 +1,44 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Areatrigger_Scripts +SD%Complete: 100 +SDComment: Scripts for areatriggers +SDCategory: Areatrigger +EndScriptData */ + +/* ContentData +at_test script test only +EndContentData */ + +#include "precompiled.h" + +bool ATtest(Player *player, AreaTriggerEntry *at) +{ + player->Say("Hi!",LANG_UNIVERSAL); + return true; +} + +void AddSC_areatrigger_scripts() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="at_test"; + newscript->pAreaTrigger = ATtest; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/boss/boss_emeriss.cpp b/src/bindings/scripts/scripts/boss/boss_emeriss.cpp new file mode 100644 index 00000000000..0870be567c5 --- /dev/null +++ b/src/bindings/scripts/scripts/boss/boss_emeriss.cpp @@ -0,0 +1,156 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Emeriss +SD%Complete: 90 +SDComment: Teleport function & Mark of Nature missing +SDCategory: Bosses +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SLEEP 24777 +#define SPELL_NOXIOUSBREATH 24818 +#define SPELL_TAILSWEEP 15847 +//#define SPELL_MARKOFNATURE 25040 // Not working +#define SPELL_VOLATILEINFECTION 24928 +#define SPELL_CORRUPTIONOFEARTH 24910 + +struct MANGOS_DLL_DECL boss_emerissAI : public ScriptedAI +{ + boss_emerissAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Sleep_Timer; + uint32 NoxiousBreath_Timer; + uint32 TailSweep_Timer; + //uint32 MarkOfNature_Timer; + uint32 VolatileInfection_Timer; + uint32 CorruptionofEarth1_Timer; + uint32 CorruptionofEarth2_Timer; + uint32 CorruptionofEarth3_Timer; + + void Reset() + { + Sleep_Timer = 15000 + rand()%5000; + NoxiousBreath_Timer = 8000; + TailSweep_Timer = 4000; + //MarkOfNature_Timer = 45000; + VolatileInfection_Timer = 12000; + CorruptionofEarth1_Timer = 0; + CorruptionofEarth2_Timer = 0; + CorruptionofEarth3_Timer = 0; + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Sleep_Timer + if (Sleep_Timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_SLEEP); + + Sleep_Timer = 8000 + rand()%8000; + }else Sleep_Timer -= diff; + + //NoxiousBreath_Timer + if (NoxiousBreath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_NOXIOUSBREATH); + NoxiousBreath_Timer = 14000 + rand()%6000; + }else NoxiousBreath_Timer -= diff; + + //Tailsweep every 2 seconds + if (TailSweep_Timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_TAILSWEEP); + + TailSweep_Timer = 2000; + }else TailSweep_Timer -= diff; + + //MarkOfNature_Timer + //if (MarkOfNature_Timer < diff) + //{ + // DoCast(m_creature->getVictim(),SPELL_MARKOFNATURE); + // MarkOfNature_Timer = 45000; + //}else MarkOfNature_Timer -= diff; + + //VolatileInfection_Timer + if (VolatileInfection_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_VOLATILEINFECTION); + VolatileInfection_Timer = 7000 + rand()%5000; + }else VolatileInfection_Timer -= diff; + + //CorruptionofEarth_Timer + if ( (int) (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() +0.5) == 75) + { + if (CorruptionofEarth1_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CORRUPTIONOFEARTH); + + //1 minutes for next one. Means not again with this health value + CorruptionofEarth1_Timer = 60000; + } else CorruptionofEarth1_Timer -= diff; + } + + //CorruptionofEarth_Timer + if ( (int) (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() +0.5) == 50) + { + if (CorruptionofEarth2_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CORRUPTIONOFEARTH); + + //1 minutes for next one. Means not again with this health value + CorruptionofEarth2_Timer = 60000; + } else CorruptionofEarth2_Timer -= diff; + } + + //CorruptionofEarth_Timer + if ( (int) (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() +0.5) == 25) + { + if (CorruptionofEarth3_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CORRUPTIONOFEARTH); + + //1 minutes for next one. Means not again with this health value + CorruptionofEarth3_Timer = 60000; + } else CorruptionofEarth3_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_emeriss(Creature *_Creature) +{ + return new boss_emerissAI (_Creature); +} + +void AddSC_boss_emeriss() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_emeriss"; + newscript->GetAI = GetAI_boss_emeriss; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/boss/boss_lethon.cpp b/src/bindings/scripts/scripts/boss/boss_lethon.cpp new file mode 100644 index 00000000000..66822e5a920 --- /dev/null +++ b/src/bindings/scripts/scripts/boss/boss_lethon.cpp @@ -0,0 +1,24 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Lethon +SD%Complete: 0 +SDComment: Place Holder +SDCategory: Bosses +EndScriptData */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/scripts/boss/boss_taerar.cpp b/src/bindings/scripts/scripts/boss/boss_taerar.cpp new file mode 100644 index 00000000000..8c7f8471880 --- /dev/null +++ b/src/bindings/scripts/scripts/boss/boss_taerar.cpp @@ -0,0 +1,306 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Taerar +SD%Complete: 90 +SDComment: Mark of Nature & Teleport NYI +SDCategory: Bosses +EndScriptData */ + +#include "precompiled.h" + +//Spells of Taerar + +#define SPELL_SLEEP 24777 +#define SPELL_NOXIOUSBREATH 24818 +#define SPELL_TAILSWEEP 15847 +//#define SPELL_MARKOFNATURE 25040 // Not working +#define SPELL_ARCANEBLAST 24857 +#define SPELL_BELLOWINGROAR 22686 +#define SPELL_SUMMONSHADE 24843 + +//Spells of Shades of Taerar + +#define SPELL_POSIONCLOUD 24840 +#define SPELL_POSIONBREATH 20667 + +struct MANGOS_DLL_DECL boss_taerarAI : public ScriptedAI +{ + boss_taerarAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Sleep_Timer; + uint32 NoxiousBreath_Timer; + uint32 TailSweep_Timer; + //uint32 MarkOfNature_Timer; + uint32 ArcaneBlast_Timer; + uint32 BellowingRoar_Timer; + uint32 Shades_Timer; + uint32 Summon1_Timer; + uint32 Summon2_Timer; + uint32 Summon3_Timer; + int Rand; + int RandX; + int RandY; + Creature* Summoned; + bool Shades; + + void Reset() + { + Sleep_Timer = 15000 + rand()%5000; + NoxiousBreath_Timer = 8000; + TailSweep_Timer = 4000; + //MarkOfNature_Timer = 45000; + ArcaneBlast_Timer = 12000; + BellowingRoar_Timer = 30000; + Summon1_Timer = 0; + Summon2_Timer = 0; + Summon3_Timer = 0; + Shades_Timer = 60000; //The time that Taerar is banished + Shades = false; + } + + void Aggro(Unit *who) + { + } + + void SummonShades(Unit* victim) + { + Rand = rand()%15; + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = rand()%15; + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + Summoned = DoSpawnCreature(15302, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 300000); + if(Summoned) + ((CreatureAI*)Summoned->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + if (Shades && Shades_Timer < diff) + { + //Become unbanished again + m_creature->setFaction(14); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + //m_creature->m_canMove = true; + Shades = false; + } else if (Shades) + { + Shades_Timer -= diff; + //Do nothing while banished + return; + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Sleep_Timer + if (Sleep_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_SLEEP); + + Sleep_Timer = 8000 + rand()%7000; + }else Sleep_Timer -= diff; + + //NoxiousBreath_Timer + if (NoxiousBreath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_NOXIOUSBREATH); + NoxiousBreath_Timer = 14000 + rand()%6000; + } else NoxiousBreath_Timer -= diff; + + //Tailsweep every 2 seconds + if (TailSweep_Timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_TAILSWEEP); + + TailSweep_Timer = 2000; + }else TailSweep_Timer -= diff; + + //MarkOfNature_Timer + //if (MarkOfNature_Timer < diff) + //{ + // DoCast(m_creature->getVictim(),SPELL_MARKOFNATURE); + // MarkOfNature_Timer = 45000; + //}else MarkOfNature_Timer -= diff; + + //ArcaneBlast_Timer + if (ArcaneBlast_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ARCANEBLAST); + ArcaneBlast_Timer = 7000 + rand()%5000; + }else ArcaneBlast_Timer -= diff; + + //BellowingRoar_Timer + if (BellowingRoar_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_BELLOWINGROAR); + BellowingRoar_Timer = 20000 + rand()%10000; + } else BellowingRoar_Timer -= diff; + + //Summon 3 Shades + if ( !Shades && (int) (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() +0.5) == 75) + { + if (Summon1_Timer < diff) + { + //Inturrupt any spell casting + m_creature->InterruptNonMeleeSpells(false); + m_creature->setFaction(35); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + //m_creature->m_canMove = false; + + //Cast + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + SummonShades(target); + SummonShades(target); + SummonShades(target); + Summon1_Timer = 120000; + Shades = true; + Shades_Timer = 60000; + } else Summon1_Timer -= diff; + } + + //Summon 3 Shades + if ( !Shades && (int) (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() +0.5) == 50) + { + if (Summon2_Timer < diff) + { + //Inturrupt any spell casting + m_creature->InterruptNonMeleeSpells(false); + m_creature->setFaction(35); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + //m_creature->m_canMove = false; + + //Cast + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + SummonShades(target); + SummonShades(target); + SummonShades(target); + Summon2_Timer = 120000; + Shades = true; + Shades_Timer = 60000; + } else Summon2_Timer -= diff; + } + + //Summon 3 Shades + if ( !Shades && (int) (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() +0.5) == 25) + { + if (Summon3_Timer < diff) + { + //Inturrupt any spell casting + m_creature->InterruptNonMeleeSpells(false); + m_creature->setFaction(35); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + //m_creature->m_canMove = false; + + //Cast + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + SummonShades(target); + SummonShades(target); + SummonShades(target); + Summon3_Timer = 120000; + Shades = true; + Shades_Timer = 60000; + } else Summon3_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; + +// Shades of Taerar Script + +struct MANGOS_DLL_DECL boss_shadeoftaerarAI : public ScriptedAI +{ + boss_shadeoftaerarAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 PoisonCloud_Timer; + uint32 PosionBreath_Timer; + + void Reset() + { + PoisonCloud_Timer = 8000; + PosionBreath_Timer = 12000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //PoisonCloud_Timer + if (PoisonCloud_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_POSIONCLOUD); + PoisonCloud_Timer = 30000; + }else PoisonCloud_Timer -= diff; + + //PosionBreath_Timer + if (PosionBreath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_POSIONBREATH); + PosionBreath_Timer = 12000; + }else PosionBreath_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_taerar(Creature *_Creature) +{ + return new boss_taerarAI (_Creature); +} + +CreatureAI* GetAI_boss_shadeoftaerar(Creature *_Creature) +{ + return new boss_shadeoftaerarAI (_Creature); +} + +void AddSC_boss_taerar() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_taerar"; + newscript->GetAI = GetAI_boss_taerar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_shade_of_taerar"; + newscript->GetAI = GetAI_boss_shadeoftaerar; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/boss/boss_ysondre.cpp b/src/bindings/scripts/scripts/boss/boss_ysondre.cpp new file mode 100644 index 00000000000..a5348630af2 --- /dev/null +++ b/src/bindings/scripts/scripts/boss/boss_ysondre.cpp @@ -0,0 +1,246 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Ysondre +SD%Complete: 90 +SDComment: Mark of Nature & Teleport missing +SDCategory: Bosses +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SLEEP 24777 +#define SPELL_NOXIOUSBREATH 24818 +#define SPELL_TAILSWEEP 15847 +//#define SPELL_MARKOFNATURE 25040 // Not working +#define SPELL_LIGHTNINGWAVE 24819 +#define SPELL_SUMMONDRUIDS 24795 + +//druid spells +#define SPELL_MOONFIRE 21669 + +// Ysondre script +struct MANGOS_DLL_DECL boss_ysondreAI : public ScriptedAI +{ + boss_ysondreAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Sleep_Timer; + uint32 NoxiousBreath_Timer; + uint32 TailSweep_Timer; + //uint32 MarkOfNature_Timer; + uint32 LightningWave_Timer; + uint32 SummonDruids1_Timer; + uint32 SummonDruids2_Timer; + uint32 SummonDruids3_Timer; + int Rand; + int RandX; + int RandY; + Creature* Summoned; + + void Reset() + { + Sleep_Timer = 15000 + rand()%5000; + NoxiousBreath_Timer = 8000; + TailSweep_Timer = 4000; + //MarkOfNature_Timer = 45000; + LightningWave_Timer = 12000; + SummonDruids1_Timer = 0; + SummonDruids2_Timer = 0; + SummonDruids3_Timer = 0; + } + + void Aggro(Unit *who) + { + } + + void SummonDruids(Unit* victim) + { + Rand = rand()%10; + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = rand()%10; + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + Summoned = DoSpawnCreature(15260, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 300000); + if(Summoned) + ((CreatureAI*)Summoned->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Sleep_Timer + if (Sleep_Timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_SLEEP); + + Sleep_Timer = 8000 + rand()%7000; + }else Sleep_Timer -= diff; + + //NoxiousBreath_Timer + if (NoxiousBreath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_NOXIOUSBREATH); + NoxiousBreath_Timer = 14000 + rand()%6000; + }else NoxiousBreath_Timer -= diff; + + //Tailsweep every 2 seconds + if (TailSweep_Timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_TAILSWEEP); + + TailSweep_Timer = 2000; + }else TailSweep_Timer -= diff; + + //MarkOfNature_Timer + //if (MarkOfNature_Timer < diff) + //{ + // DoCast(m_creature->getVictim(),SPELL_MARKOFNATURE); + // MarkOfNature_Timer = 45000; + //}else MarkOfNature_Timer -= diff; + + //LightningWave_Timer + if (LightningWave_Timer < diff) + { + //Cast LIGHTNINGWAVE on a Random target + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_LIGHTNINGWAVE); + + LightningWave_Timer = 7000 + rand()%5000; + }else LightningWave_Timer -= diff; + + //Summon Druids + if ( (int) (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() +0.5) == 75) + { + if (SummonDruids1_Timer < diff) + { + // summon 10 druids + Unit* target = NULL; + for(int i = 0; i < 10;i++) + { + target = SelectUnit(SELECT_TARGET_RANDOM,0); + SummonDruids(target); + } + + SummonDruids1_Timer = 60000; + } else SummonDruids1_Timer -= diff; + } + + //Summon Druids + if ( (int) (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() +0.5) == 50) + { + if (SummonDruids2_Timer < diff) + { + // summon 10 druids + Unit* target = NULL; + for(int i = 0; i < 10;i++) + { + target = SelectUnit(SELECT_TARGET_RANDOM,0); + SummonDruids(target); + } + + SummonDruids2_Timer = 60000; + } else SummonDruids2_Timer -= diff; + } + + //Summon Druids + if ( (int) (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() +0.5) == 25) + { + if (SummonDruids3_Timer < diff) + { + // summon 10 druids + Unit* target = NULL; + for(int i = 0; i < 10;i++) + { + target = SelectUnit(SELECT_TARGET_RANDOM,0); + SummonDruids(target); + } + + SummonDruids3_Timer = 60000; + } else SummonDruids3_Timer -= diff; + } + DoMeleeAttackIfReady(); + } +}; +// Summoned druid script +struct MANGOS_DLL_DECL mob_dementeddruidsAI : public ScriptedAI +{ + mob_dementeddruidsAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 MoonFire_Timer; + + void Reset() + { + MoonFire_Timer = 3000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //MoonFire_Timer + if (MoonFire_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MOONFIRE); + MoonFire_Timer = 5000; + }else MoonFire_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ysondre(Creature *_Creature) +{ + return new boss_ysondreAI (_Creature); +} + +CreatureAI* GetAI_mob_dementeddruids(Creature *_Creature) +{ + return new mob_dementeddruidsAI (_Creature); +} + +void AddSC_boss_ysondre() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_ysondre"; + newscript->GetAI = GetAI_boss_ysondre; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_dementeddruids"; + newscript->GetAI = GetAI_mob_dementeddruids; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/creature/mob_event_ai.cpp b/src/bindings/scripts/scripts/creature/mob_event_ai.cpp new file mode 100644 index 00000000000..f86931df766 --- /dev/null +++ b/src/bindings/scripts/scripts/creature/mob_event_ai.cpp @@ -0,0 +1,1389 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Mob_Event_AI +SD%Complete: 90 +SDComment: Database Event AI +SDCategory: Creatures +EndScriptData */ + +#include "precompiled.h" +#include "mob_event_ai.h" + +#define EVENT_UPDATE_TIME 500 +#define SPELL_RUN_AWAY 8225 + +struct EventHolder +{ + EventHolder(EventAI_Event p) : Event(p), Time(0), Enabled(true){} + + EventAI_Event Event; + uint32 Time; + bool Enabled; +}; + +struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI +{ + Mob_EventAI(Creature *c, std::list pEventList) : ScriptedAI(c) + { + EventList = pEventList; + Phase = 0; + CombatMovementEnabled = true; + MeleeEnabled = true; + AttackDistance = 0; + AttackAngle = 0.0f; + + //Handle Spawned Events + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + case EVENT_T_SPAWNED: + ProcessEvent(*i); + break; + } + } + + Reset(); + } + + ~Mob_EventAI() + { + EventList.clear(); + } + + //Variables used by EventAI for handling events + std::list EventList; //Holder for events (stores enabled, time, and eventid) + uint32 EventUpdateTime; //Time between event updates + uint32 EventDiff; //Time between the last event call + + //Variables used by Events themselves + uint8 Phase; //Current phase, max 32 phases + bool CombatMovementEnabled; //If we allow targeted movment gen (movement twoards top threat) + bool MeleeEnabled; //If we allow melee auto attack + uint32 AttackDistance; //Distance to attack from + float AttackAngle; //Angle of attack + + void AttackTarget(Unit* pTarget, bool Follow) + { + if (!pTarget) + return; + + if ( m_creature->Attack(pTarget, true) ) + { + if (Follow) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveChase(pTarget, AttackDistance, AttackAngle); + } + + m_creature->AddThreat(pTarget, 0.0f); + } + } + + bool ProcessEvent(EventHolder& pHolder, Unit* pActionInvoker = NULL) + { + if (!pHolder.Enabled || pHolder.Time) + return false; + + //Check the inverse phase mask (event doesn't trigger if current phase bit is set in mask) + if (pHolder.Event.event_inverse_phase_mask & (1 << Phase)) + return false; + + //Store random here so that all random actions match up + uint32 rnd = rand(); + + //Return if chance for event is not met + if (pHolder.Event.event_chance <= rnd % 100) + return false; + + union + { + uint32 param1; + int32 param1_s; + }; + + union + { + uint32 param2; + int32 param2_s; + }; + + union + { + uint32 param3; + int32 param3_s; + }; + + union + { + uint32 param4; + int32 param4_s; + }; + + param1 = pHolder.Event.event_param1; + param2 = pHolder.Event.event_param2; + param3 = pHolder.Event.event_param3; + param4 = pHolder.Event.event_param4; + + //Check event conditions based on the event type, also reset events + switch (pHolder.Event.event_type) + { + case EVENT_T_TIMER: + { + if (!InCombat) + return false; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_TIMER_OOC: + { + if (InCombat) + return false; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_HP: + { + if (!InCombat || !m_creature->GetMaxHealth()) + return false; + + uint32 perc = (m_creature->GetHealth()*100) / m_creature->GetMaxHealth(); + + if (perc > param1 || perc < param2) + return false; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_MANA: + { + if (!InCombat || !m_creature->GetMaxPower(POWER_MANA)) + return false; + + uint32 perc = (m_creature->GetPower(POWER_MANA)*100) / m_creature->GetMaxPower(POWER_MANA); + + if (perc > param1 || perc < param2) + return false; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_AGGRO: + { + } + break; + case EVENT_T_KILL: + { + //Repeat Timers + if (param1 == param2) + { + pHolder.Time = param1; + + }else if (param2 > param1) + pHolder.Time = urand(param1, param2); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + case EVENT_T_DEATH: + { + } + break; + case EVENT_T_EVADE: + { + } + break; + case EVENT_T_SPELLHIT: + { + //Spell hit is special case, param1 and param2 handled within EventAI::SpellHit + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_RANGE: + { + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_OOC_LOS: + { + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_SPAWNED: + { + } + break; + case EVENT_T_TARGET_HP: + { + if (!InCombat || !m_creature->getVictim() || !m_creature->getVictim()->GetMaxHealth()) + return false; + + uint32 perc = (m_creature->getVictim()->GetHealth()*100) / m_creature->getVictim()->GetMaxHealth(); + + if (perc > param1 || perc < param2) + return false; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_TARGET_CASTING: + { + if (!InCombat || !m_creature->getVictim() || !m_creature->getVictim()->IsNonMeleeSpellCasted(false, false, true)) + return false; + + //Repeat Timers + if (param1 == param2) + { + pHolder.Time = param1; + + }else if (param2 > param1) + pHolder.Time = urand(param1, param2); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + case EVENT_T_FRIENDLY_HP: + { + if (!InCombat) + return false; + + Unit* pUnit = DoSelectLowestHpFriendly(param2, param1); + + if (!pUnit) + return false; + + pActionInvoker = pUnit; + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + + case EVENT_T_FRIENDLY_IS_CC: + { + if (!InCombat) + return false; + + std::list pList = DoFindFriendlyCC(param2); + + //List is empty + if (pList.empty()) + return false; + + //We don't really care about the whole list, just return first available + pActionInvoker = *(pList.begin()); + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + + case EVENT_T_FRIENDLY_MISSING_BUFF: + { + std::list pList = DoFindFriendlyMissingBuff(param2, param1); + + //List is empty + if (pList.empty()) + return false; + + //We don't really care about the whole list, just return first available + pActionInvoker = *(pList.begin()); + + //Repeat Timers + if (param3 == param4) + { + pHolder.Time = param3; + + }else if (param4 > param3) + pHolder.Time = urand(param3, param4); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + + case EVENT_T_SUMMONED_UNIT: + { + //Prevent event from occuring on no unit or non creatures + if (!pActionInvoker || pActionInvoker->GetTypeId()!=TYPEID_UNIT) + return false; + + //Creature id doesn't match up + if (param1 && ((Creature*)pActionInvoker)->GetEntry() != param1) + return false; + + //Repeat Timers + if (param2 == param3) + { + pHolder.Time = param2; + + }else if (param3 > param2) + pHolder.Time = urand(param2, param3); + else + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has RandomMax < RandomMin. Event repeating disabled.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + pHolder.Enabled = false; + } + } + break; + default: + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u has invalid Event Type(%u), missing from ProcessEvent() Switch.", m_creature->GetEntry(), pHolder.Event.event_id, pHolder.Event.event_type); + break; + } + + //Disable non-repeatable events + if (!(pHolder.Event.event_flags & EFLAG_REPEATABLE)) + pHolder.Enabled = false; + + //Process actions + for (uint32 j = 0; j < MAX_ACTIONS; j++) + ProcessAction(pHolder.Event.action[j].type, pHolder.Event.action[j].param1, pHolder.Event.action[j].param2, pHolder.Event.action[j].param3, rnd, pHolder.Event.event_id, pActionInvoker); + + return true; + } + + inline uint32 GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3) + { + switch (rnd % 3) + { + case 0: + return param1; + break; + + case 1: + return param2; + break; + + case 2: + return param3; + break; + } + return 0; + } + + inline Unit* GetTargetByType(uint32 Target, Unit* pActionInvoker) + { + switch (Target) + { + case TARGET_T_SELF: + return m_creature; + break; + case TARGET_T_HOSTILE: + return m_creature->getVictim(); + break; + case TARGET_T_HOSTILE_SECOND_AGGRO: + return SelectUnit(SELECT_TARGET_TOPAGGRO,1); + break; + case TARGET_T_HOSTILE_LAST_AGGRO: + return SelectUnit(SELECT_TARGET_BOTTOMAGGRO,0); + break; + case TARGET_T_HOSTILE_RANDOM: + return SelectUnit(SELECT_TARGET_RANDOM,0); + break; + case TARGET_T_HOSTILE_RANDOM_NOT_TOP: + return SelectUnit(SELECT_TARGET_RANDOM,1); + break; + case TARGET_T_ACTION_INVOKER: + return pActionInvoker; + break; + default: + return NULL; + break; + }; + } + + void ProcessAction(uint16 type, uint32 param1, uint32 param2, uint32 param3, uint32 rnd, uint32 EventId, Unit* pActionInvoker) + { + switch (type) + { + case ACTION_T_SAY: + DoSay(GetEventAIText(param1), LANG_UNIVERSAL, pActionInvoker ? pActionInvoker : m_creature->getVictim()); + break; + + case ACTION_T_YELL: + DoYell(GetEventAIText(param1), LANG_UNIVERSAL, pActionInvoker ? pActionInvoker : m_creature->getVictim()); + break; + + case ACTION_T_TEXTEMOTE: + DoTextEmote(GetEventAIText(param1), pActionInvoker ? pActionInvoker : m_creature->getVictim()); + break; + + case ACTION_T_SOUND: + DoPlaySoundToSet(m_creature, param1); + break; + + case ACTION_T_EMOTE: + m_creature->HandleEmoteCommand(param1); + break; + + case ACTION_T_RANDOM_SAY: + { + uint32 temp = GetRandActionParam(rnd, param1, param2, param3); + + if (temp != 0xffffffff) + DoSay(GetEventAIText(temp), LANG_UNIVERSAL, pActionInvoker ? pActionInvoker : m_creature->getVictim()); + } + break; + + case ACTION_T_RANDOM_YELL: + { + uint32 temp = GetRandActionParam(rnd, param1, param2, param3); + + if (temp != 0xffffffff) + DoYell(GetEventAIText(temp), LANG_UNIVERSAL, pActionInvoker ? pActionInvoker : m_creature->getVictim()); + } + break; + + case ACTION_T_RANDOM_TEXTEMOTE: + { + uint32 temp = GetRandActionParam(rnd, param1, param2, param3); + + if (temp != 0xffffffff) + DoTextEmote(GetEventAIText(temp), pActionInvoker ? pActionInvoker : m_creature->getVictim()); + } + break; + + case ACTION_T_RANDOM_SOUND: + { + uint32 temp = GetRandActionParam(rnd, param1, param2, param3); + + if (temp != 0xffffffff) + DoPlaySoundToSet(m_creature, temp); + } + break; + + case ACTION_T_RANDOM_EMOTE: + { + uint32 temp = GetRandActionParam(rnd, param1, param2, param3); + + if (temp != 0xffffffff) + m_creature->HandleEmoteCommand(temp); + } + break; + + case ACTION_T_CAST: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + Unit* caster = m_creature; + + if (!target) + return; + + //Cast is always triggered if target is forced to cast on self + if (param3 & CAST_FORCE_TARGET_SELF) + { + param3 |= CAST_TRIGGERED; + caster = target; + } + + //Interrupt any previous spell + if (caster->IsNonMeleeSpellCasted(false) && param3 & CAST_INTURRUPT_PREVIOUS) + caster->InterruptNonMeleeSpells(false); + + //Cast only if not casting or if spell is triggered + if (param3 & CAST_TRIGGERED || !caster->IsNonMeleeSpellCasted(false)) + { + const SpellEntry* tSpell = GetSpellStore()->LookupEntry(param1); + + //Verify that spell exists + if (tSpell) + { + //Check if cannot cast spell + if (!(param3 & (CAST_FORCE_TARGET_SELF | CAST_FORCE_CAST)) && + !CanCast(target, tSpell, (param3 & CAST_TRIGGERED))) + { + //Melee current victim if flag not set + if (!(param3 & CAST_NO_MELEE_IF_OOM)) + { + AttackDistance = 0; + AttackAngle = 0; + + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle); + } + + }else caster->CastSpell(target, param1, (param3 & CAST_TRIGGERED)); + + }else if (EAI_ErrorLevel > 0) + error_db_log("SD2: EventAI event %d creature %d attempt to cast spell that doesn't exist %d", EventId, m_creature->GetEntry(), param1); + } + } + break; + + case ACTION_T_SUMMON: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + Creature* pCreature = NULL; + + if (param3) + pCreature = DoSpawnCreature(param1, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, param3); + else pCreature = pCreature = DoSpawnCreature(param1, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0); + + if (!pCreature) + { + if (EAI_ErrorLevel > 0) + error_db_log( "SD2: EventAI failed to spawn creature %u. Spawn event %d is on creature %d", param1, EventId, m_creature->GetEntry()); + } + else if (param2 != TARGET_T_SELF && target) + pCreature->AI()->AttackStart(target); + } + break; + + case ACTION_T_THREAT_SINGLE_PCT: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + if (target) + m_creature->getThreatManager().modifyThreatPercent(target, param1); + } + break; + + case ACTION_T_THREAT_ALL_PCT: + { + Unit* Temp = NULL; + + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for (; i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + Temp = Unit::GetUnit((*m_creature),(*i)->getUnitGuid()); + if (Temp) + m_creature->getThreatManager().modifyThreatPercent(Temp, param1); + } + } + break; + + case ACTION_T_QUEST_EVENT: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + if (target && target->GetTypeId() == TYPEID_PLAYER) + ((Player*)target)->AreaExploredOrEventHappens(param1); + } + break; + + case ACTION_T_CASTCREATUREGO: + { + Unit* target = GetTargetByType(param3, pActionInvoker); + + if (target && target->GetTypeId() == TYPEID_PLAYER) + ((Player*)target)->CastedCreatureOrGO(param1, m_creature->GetGUID(), param2); + } + break; + + case ACTION_T_SET_UNIT_FIELD: + { + Unit* target = GetTargetByType(param3, pActionInvoker); + + if (target) + target->SetUInt32Value(param1, param2); + } + break; + + case ACTION_T_SET_UNIT_FLAG: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + if (target) + target->SetFlag(UNIT_FIELD_FLAGS, param1); + } + break; + + case ACTION_T_REMOVE_UNIT_FLAG: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + if (target) + target->RemoveFlag(UNIT_FIELD_FLAGS, param1); + } + break; + + case ACTION_T_AUTO_ATTACK: + { + if (param1) + MeleeEnabled = true; + else MeleeEnabled = false; + } + break; + + case ACTION_T_COMBAT_MOVEMENT: + { + CombatMovementEnabled = param1; + + //Allow movement (create new targeted movement gen if none exist already) + if (CombatMovementEnabled) + { + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle); + } + } + else + if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->StopMoving(); + } + } + break; + case ACTION_T_SET_PHASE: + { + Phase = param1; + } + break; + case ACTION_T_INC_PHASE: + { + Phase += param1; + + if (Phase > 31) + if (EAI_ErrorLevel > 0) + error_db_log( "SD2: Event %d incremented Phase above 31. Phase mask cannot be used with phases past 31. CreatureEntry = %d", EventId, m_creature->GetEntry()); + } + break; + + case ACTION_T_EVADE: + { + EnterEvadeMode(); + } + break; + case ACTION_T_FLEE: + { + //TODO: Replace with Flee movement generator + m_creature->CastSpell(m_creature, SPELL_RUN_AWAY, true); + } + break; + case ACTION_T_QUEST_EVENT_ALL: + { + Unit* Temp = NULL; + if( pActionInvoker && pActionInvoker->GetTypeId() == TYPEID_PLAYER ) + { + Temp = Unit::GetUnit((*m_creature),pActionInvoker->GetGUID()); + if( Temp ) + ((Player*)Temp)->GroupEventHappens(param1,m_creature); + } + } + break; + + case ACTION_T_CASTCREATUREGO_ALL: + { + Unit* Temp = NULL; + + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for (; i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + Temp = Unit::GetUnit((*m_creature),(*i)->getUnitGuid()); + if (Temp && Temp->GetTypeId() == TYPEID_PLAYER) + ((Player*)Temp)->CastedCreatureOrGO(param1, m_creature->GetGUID(), param2); + } + } + break; + + case ACTION_T_REMOVEAURASFROMSPELL: + { + Unit* target = GetTargetByType(param1, pActionInvoker); + + if (target) + target->RemoveAurasDueToSpell(param2); + } + break; + + case ACTION_T_RANGED_MOVEMENT: + { + AttackDistance = param1; + AttackAngle = ((float)param2/180)*M_PI; + + if (CombatMovementEnabled) + { + //Drop current movement gen + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle); + } + } + break; + + case ACTION_T_RANDOM_PHASE: + { + uint32 temp = GetRandActionParam(rnd, param1, param2, param3); + + Phase = temp; + } + break; + + case ACTION_T_RANDOM_PHASE_RANGE: + { + if (param2 > param1) + { + Phase = param1 + (rnd % (param2 - param1)); + } + else if (EAI_ErrorLevel > 0) + error_db_log( "SD2: ACTION_T_RANDOM_PHASE_RANGE cannot have Param2 <= Param1. Divide by Zero. Event = %d. CreatureEntry = %d", EventId, m_creature->GetEntry()); + } + break; + case ACTION_T_SUMMON_ID: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + //Duration + Creature* pCreature = NULL; + + HM_NAMESPACE::hash_map::iterator i = EventAI_Summon_Map.find(param3); + + if (i == EventAI_Summon_Map.end()) + { + if (EAI_ErrorLevel > 0) + error_db_log( "SD2: EventAI failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", param1, param3, EventId, m_creature->GetEntry()); + return; + } + + if ((*i).second.SpawnTimeSecs) + pCreature = m_creature->SummonCreature(param1, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, (*i).second.SpawnTimeSecs); + else pCreature = m_creature->SummonCreature(param1, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0); + + if (!pCreature) + { + if (EAI_ErrorLevel > 0) + error_db_log( "SD2: EventAI failed to spawn creature %u. EventId %d.Creature %d", param1, EventId, m_creature->GetEntry()); + } + else if (param2 != TARGET_T_SELF && target) + pCreature->AI()->AttackStart(target); + } + break; + + case ACTION_T_KILLED_MONSTER: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + if (target && target->GetTypeId() == TYPEID_PLAYER) + ((Player*)target)->KilledMonster(param1, m_creature->GetGUID()); + } + break; + + case ACTION_T_SET_INST_DATA: + { + ScriptedInstance* pInst = (ScriptedInstance*)m_creature->GetInstanceData(); + if (!pInst) + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Event %d attempt to set instance data without instance script. Creature %d", EventId, m_creature->GetEntry()); + return; + } + + pInst->SetData(param1, param2); + } + break; + + case ACTION_T_SET_INST_DATA64: + { + Unit* target = GetTargetByType(param2, pActionInvoker); + + if (!target) + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Event %d attempt to set instance data64 but Target == NULL. Creature %d", EventId, m_creature->GetEntry()); + return; + } + + ScriptedInstance* pInst = (ScriptedInstance*)m_creature->GetInstanceData(); + + if (!pInst) + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Event %d attempt to set instance data64 without instance script. Creature %d", EventId, m_creature->GetEntry()); + return; + } + + pInst->SetData64(param1, target->GetGUID()); + } + break; + + case ACTION_T_UPDATE_TEMPLATE: + { + if (m_creature->GetEntry() == param1) + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d", EventId, m_creature->GetEntry()); + return; + } + + m_creature->UpdateEntry(param1, param2 ? HORDE : ALLIANCE); + } + break; + + case ACTION_T_DIE: + { + if (m_creature->isDead()) + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Event %d ACTION_T_DIE on dead creature. Creature %d", EventId, m_creature->GetEntry()); + return; + } + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(),NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + break; + + case ACTION_T_ZONE_COMBAT_PULSE: + { + if (!m_creature->isInCombat() || !m_creature->GetMap()->IsDungeon()) + { + if (EAI_ErrorLevel > 0) + error_db_log("SD2: Event %d ACTION_T_ZONE_COMBAT_PULSE on creature out of combat or in non-dungeon map. Creature %d", EventId, m_creature->GetEntry()); + return; + } + + DoZoneInCombat(); + } + break; + }; + } + + void JustRespawned() + { + InCombat = false; + Reset(); + + //Handle Spawned Events + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + case EVENT_T_SPAWNED: + ProcessEvent(*i); + break; + } + } + } + + void Reset() + { + EventUpdateTime = EVENT_UPDATE_TIME; + EventDiff = 0; + + //Handle Evade events and reset all events to enabled + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + //Reset all out of combat timers + case EVENT_T_TIMER_OOC: + { + if ((*i).Event.event_param2 == (*i).Event.event_param1) + { + (*i).Time = (*i).Event.event_param1; + (*i).Enabled = true; + + }else if ((*i).Event.event_param2 > (*i).Event.event_param1) + { + (*i).Time = urand((*i).Event.event_param1, (*i).Event.event_param2); + (*i).Enabled = true; + }else if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature->GetEntry(), (*i).Event.event_id, (*i).Event.event_type); + + }break; + } + } + } + + void EnterEvadeMode() + { + 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(); + + //Handle Evade events + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + //Evade + case EVENT_T_EVADE: + ProcessEvent(*i); + break; + } + } + } + + void JustDied(Unit* killer) + { + InCombat = false; + Reset(); + + //Handle Evade events + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + //Evade + case EVENT_T_DEATH: + ProcessEvent(*i, killer); + break; + } + } + } + + void KilledUnit(Unit* victim) + { + if (victim->GetTypeId() != TYPEID_PLAYER) + return; + + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + //Kill + case EVENT_T_KILL: + ProcessEvent(*i, victim); + break; + } + } + + } + + void JustSummoned(Creature* pUnit) + { + if (!pUnit) + return; + + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + //Summoned + case EVENT_T_SUMMONED_UNIT: + ProcessEvent(*i, pUnit); + break; + } + } + } + + void Aggro(Unit *who) + { + //Check for on combat start events + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + case EVENT_T_AGGRO: + ProcessEvent(*i, who); + break; + + //Reset all in combat timers + case EVENT_T_TIMER: + + if ((*i).Event.event_param2 == (*i).Event.event_param1) + { + (*i).Time = (*i).Event.event_param1; + (*i).Enabled = true; + + }else if ((*i).Event.event_param2 > (*i).Event.event_param1) + { + (*i).Time = urand((*i).Event.event_param1, (*i).Event.event_param2); + (*i).Enabled = true; + }else if (EAI_ErrorLevel > 0) + error_db_log("SD2: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature->GetEntry(), (*i).Event.event_id, (*i).Event.event_type); + + break; + + //All normal events need to be re-enabled and their time set to 0 + default: + (*i).Enabled = true; + (*i).Time = 0; + break; + } + } + + EventUpdateTime = EVENT_UPDATE_TIME; + EventDiff = 0; + } + + void AttackStart(Unit *who) + { + if (!who) + return; + + if (who->isTargetableForAttack()) + { + //Begin melee attack if we are within range + if (CombatMovementEnabled) + AttackTarget(who, true); + else AttackTarget(who, false); + + if (!InCombat) + { + Aggro(who); + InCombat = true; + } + } + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || InCombat) + return; + + //Check for OOC LOS Event + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + case EVENT_T_OOC_LOS: + { + if ((*i).Event.event_param1 && m_creature->IsHostileTo(who)) + break; + + if ((*i).Event.event_param2 && !m_creature->IsHostileTo(who)) + break; + + ProcessEvent(*i, who); + } + break; + } + } + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + //Begin melee attack if we are within range + if (CombatMovementEnabled) + AttackTarget(who, true); + else AttackTarget(who, false); + + if (!InCombat) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void SpellHit(Unit* pUnit, const SpellEntry* pSpell) + { + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + //Spell hit + case EVENT_T_SPELLHIT: + { + //If spell id matches (or no spell id) & if spell school matches (or no spell school) + if (!(*i).Event.event_param1 || pSpell->Id == (*i).Event.event_param1) + if ((*i).Event.event_param2_s == -1 || pSpell->SchoolMask == (*i).Event.event_param2) + ProcessEvent(*i, pUnit); + } + break; + } + } + } + + void UpdateAI(const uint32 diff) + { + //Check if we are in combat (also updates calls threat update code) + bool Combat = InCombat ? (m_creature->SelectHostilTarget() && m_creature->getVictim()) : false; + + //Must return if creature isn't alive. Normally select hostil target and get victim prevent this + if (!m_creature->isAlive()) + return; + + //Events are only updated once every EVENT_UPDATE_TIME ms to prevent lag with large amount of events + if (EventUpdateTime < diff) + { + EventDiff += diff; + + //Check for range based events + //if (m_creature->GetDistance(m_creature->getVictim()) > + if (Combat) + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + switch ((*i).Event.event_type) + { + case EVENT_T_RANGE: + float dist = m_creature->GetDistance(m_creature->getVictim()); + if (dist > (*i).Event.event_param1 && dist < (*i).Event.event_param2) + ProcessEvent(*i); + break; + } + } + + //Check for time based events + for (std::list::iterator i = EventList.begin(); i != EventList.end(); ++i) + { + //Decrement Timers + if ((*i).Time) + if ((*i).Time > EventDiff) + { + //Do not decrement timers if event cannot trigger in this phase + if (!((*i).Event.event_inverse_phase_mask & (1 << Phase))) + (*i).Time -= EventDiff; + + //Skip processing of events that have time remaining + continue; + } + else (*i).Time = 0; + + switch ((*i).Event.event_type) + { + //Events that are updated every EVENT_UPDATE_TIME + case EVENT_T_TIMER: + case EVENT_T_TIMER_OOC: + case EVENT_T_MANA: + case EVENT_T_HP: + case EVENT_T_TARGET_HP: + case EVENT_T_TARGET_CASTING: + case EVENT_T_FRIENDLY_HP: + ProcessEvent(*i); + break; + } + } + + EventDiff = 0; + EventUpdateTime = EVENT_UPDATE_TIME; + }else + { + EventDiff += diff; + EventUpdateTime -= diff; + } + + //Melee Auto-Attack + if (Combat && MeleeEnabled) + DoMeleeAttackIfReady(); + + } +}; + +CreatureAI* GetAI_Mob_EventAI(Creature *_Creature) +{ + //Select events by creature id + std::list EventList; + uint32 ID = _Creature->GetEntry(); + + std::list::iterator i; + + for (i = EventAI_Event_List.begin(); i != EventAI_Event_List.end(); ++i) + { + if ((*i).creature_id == ID) + { + //Debug check +#ifndef _DEBUG + if ((*i).event_flags & EFLAG_DEBUG_ONLY) + continue; +#endif + + //Heroic Instance Difficulty check + if ((*i).event_flags & EFLAG_HEROIC) + { + //Creature isn't even in a dungeon + if (!_Creature->GetMap() || !_Creature->GetMap()->IsDungeon()) + { + if (EAI_ErrorLevel > 1) + error_db_log("SD2: Creature %u, Event %u using EFLAG_HEROIC but creature is not inside of an instance. Event skipped.", _Creature->GetEntry(), (*i).event_id); + continue; + } + + if (!_Creature->GetMap()->IsHeroic()) + continue; + } else + + //Normal Instance Difficulty check + if ((*i).event_flags & EFLAG_NORMAL) + { + //Creature isn't even in a dungeon + if (!_Creature->GetMap() || !_Creature->GetMap()->IsDungeon()) + { + if (EAI_ErrorLevel > 1) + error_db_log("SD2: Creature %u, Event %u using EFLAG_NORMAL but creature is not inside of an instance. Event skipped.", _Creature->GetEntry(), (*i).event_id); + continue; + } + + if (_Creature->GetMap()->IsHeroic()) + continue; + } else if (_Creature->GetMap() && _Creature->GetMap()->IsDungeon() && EAI_ErrorLevel > 1) + error_db_log("SD2: Creature %u, Event %u. Creature is in instance but neither EFLAG_NORMAL or EFLAG_HEROIC are set.", _Creature->GetEntry(), (*i).event_id); + + //Push back event + EventList.push_back(EventHolder(*i)); + } + } + + //EventAI is pointless to use without events and may cause crashes + if (EventList.empty()) + { + if (EAI_ErrorLevel > 1) + error_db_log("SD2: Eventlist for Creature %u is empty but creature is using Mob_EventAI. Preventing EventAI on this creature.", _Creature->GetEntry()); + + return NULL; + } + + return new Mob_EventAI (_Creature, EventList); +} + +void AddSC_mob_event() +{ + Script *newscript; + newscript = new Script; + newscript->Name="mob_eventai"; + newscript->GetAI = GetAI_Mob_EventAI; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/creature/mob_event_ai.h b/src/bindings/scripts/scripts/creature/mob_event_ai.h new file mode 100644 index 00000000000..05b0b6d7b45 --- /dev/null +++ b/src/bindings/scripts/scripts/creature/mob_event_ai.h @@ -0,0 +1,215 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_EVENTAI_H +#define SC_EVENTAI_H + +#define MAX_ACTIONS 3 + +enum Event_Types +{ + EVENT_T_TIMER = 0, //InitialMin, InitialMax, RepeatMin, RepeatMax + EVENT_T_TIMER_OOC = 1, //InitialMin, InitialMax, RepeatMin, RepeatMax + EVENT_T_HP = 2, //HPMax%, HPMin%, RepeatMin, RepeatMax + EVENT_T_MANA = 3, //ManaMax%,ManaMin% RepeatMin, RepeatMax + EVENT_T_AGGRO = 4, //NONE + EVENT_T_KILL = 5, //RepeatMin, RepeatMax + EVENT_T_DEATH = 6, //NONE + EVENT_T_EVADE = 7, //NONE + EVENT_T_SPELLHIT = 8, //SpellID, School, RepeatMin, RepeatMax + EVENT_T_RANGE = 9, //MinDist, MaxDist, RepeatMin, RepeatMax + EVENT_T_OOC_LOS = 10, //NoHostile, NoFriendly, RepeatMin, RepeatMax + EVENT_T_SPAWNED = 11, //NONE + EVENT_T_TARGET_HP = 12, //HPMax%, HPMin%, RepeatMin, RepeatMax + EVENT_T_TARGET_CASTING = 13, //RepeatMin, RepeatMax + EVENT_T_FRIENDLY_HP = 14, //HPDeficit, Radius, RepeatMin, RepeatMax + EVENT_T_FRIENDLY_IS_CC = 15, //DispelType, Radius, RepeatMin, RepeatMax + EVENT_T_FRIENDLY_MISSING_BUFF = 16, //SpellId, Radius, RepeatMin, RepeatMax + EVENT_T_SUMMONED_UNIT = 17, //CreatureId, RepeatMin, RepeatMax + EVENT_T_TARGET_MANA = 18, //ManaMax%, ManaMin%, RepeatMin, RepeatMax + EVENT_T_QUEST_ACCEPT = 19, //QuestID + EVENT_T_QUEST_COMPLETE = 20, // + + EVENT_T_END, +}; + +enum Action_Types +{ + ACTION_T_NONE = 0, //No action + ACTION_T_SAY = 1, //TextId + ACTION_T_YELL = 2, //TextId + ACTION_T_TEXTEMOTE = 3, //TextId + ACTION_T_SOUND = 4, //SoundId + ACTION_T_EMOTE = 5, //EmoteId + ACTION_T_RANDOM_SAY = 6, //TextId1, TextId2, TextId3 (-1 in any field means no output if randomed that field) + ACTION_T_RANDOM_YELL = 7, //TextId1, TextId2, TextId3 (-1 in any field means no output if randomed that field) + ACTION_T_RANDOM_TEXTEMOTE = 8, //TextId1, TextId2, TextId3 (-1 in any field means no output if randomed that field) + ACTION_T_RANDOM_SOUND = 9, //SoundId1, SoundId2, SoundId3 (-1 in any field means no output if randomed that field) + ACTION_T_RANDOM_EMOTE = 10, //EmoteId1, EmoteId2, EmoteId3 (-1 in any field means no output if randomed that field) + ACTION_T_CAST = 11, //SpellId, Target, CastFlags + ACTION_T_SUMMON = 12, //CreatureID, Target, Duration in ms + ACTION_T_THREAT_SINGLE_PCT = 13, //Threat%, Target + ACTION_T_THREAT_ALL_PCT = 14, //Threat% + ACTION_T_QUEST_EVENT = 15, //QuestID, Target + ACTION_T_CASTCREATUREGO = 16, //QuestID, SpellId, Target + ACTION_T_SET_UNIT_FIELD = 17, //Field_Number, Value, Target + ACTION_T_SET_UNIT_FLAG = 18, //Flags (may be more than one field OR'd together), Target + ACTION_T_REMOVE_UNIT_FLAG = 19, //Flags (may be more than one field OR'd together), Target + ACTION_T_AUTO_ATTACK = 20, //AllowAttackState (0 = stop attack, anything else means continue attacking) + ACTION_T_COMBAT_MOVEMENT = 21, //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) + ACTION_T_SET_PHASE = 22, //Phase + ACTION_T_INC_PHASE = 23, //Value (may be negative to decrement phase, should not be 0) + ACTION_T_EVADE = 24, //No Params + ACTION_T_FLEE = 25, //No Params + ACTION_T_QUEST_EVENT_ALL = 26, //QuestID + ACTION_T_CASTCREATUREGO_ALL = 27, //QuestId, SpellId + ACTION_T_REMOVEAURASFROMSPELL = 28, //Target, Spellid + ACTION_T_RANGED_MOVEMENT = 29, //Distance, Angle + ACTION_T_RANDOM_PHASE = 30, //PhaseId1, PhaseId2, PhaseId3 + ACTION_T_RANDOM_PHASE_RANGE = 31, //PhaseMin, PhaseMax + ACTION_T_SUMMON_ID = 32, //CreatureId, Target, SpawnId + ACTION_T_KILLED_MONSTER = 33, //CreatureId, Target + ACTION_T_SET_INST_DATA = 34, //Field, Data + ACTION_T_SET_INST_DATA64 = 35, //Field, Target + ACTION_T_UPDATE_TEMPLATE = 36, //Entry, Team + ACTION_T_DIE = 37, //No Params + ACTION_T_ZONE_COMBAT_PULSE = 38, //No Params + + ACTION_T_END, +}; + +enum Target +{ + //Self (m_creature) + TARGET_T_SELF = 0, //Self cast + + //Hostile targets (if pet then returns pet owner) + TARGET_T_HOSTILE, //Our current target (ie: highest aggro) + TARGET_T_HOSTILE_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks) + TARGET_T_HOSTILE_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for) + TARGET_T_HOSTILE_RANDOM, //Just any random target on our threat list + TARGET_T_HOSTILE_RANDOM_NOT_TOP, //Any random target except top threat + + //Invoker targets (if pet then returns pet owner) + 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, EVENT_T_FRIENDLY_IS_CC, EVENT_T_FRIENDLY_MISSING_BUFF) + + //Hostile targets (including pets) + TARGET_T_HOSTILE_WPET, //Current target (can be a pet) + TARGET_T_HOSTILE_WPET_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks) + TARGET_T_HOSTILE_WPET_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for) + TARGET_T_HOSTILE_WPET_RANDOM, //Just any random target on our threat list + TARGET_T_HOSTILE_WPET_RANDOM_NOT_TOP, //Any random target except top threat + + TARGET_T_ACTION_INVOKER_WPET, + + TARGET_T_END +}; + +enum CastFlags +{ + CAST_INTURRUPT_PREVIOUS = 0x01, //Interrupt any spell casting + CAST_TRIGGERED = 0x02, //Triggered (this makes spell cost zero mana and have no cast time) + CAST_FORCE_CAST = 0x04, //Forces cast even if creature is out of mana or out of range + CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range + CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself +}; + +enum EventFlags +{ + EFLAG_REPEATABLE = 0x01, //Event repeats + EFLAG_NORMAL = 0x02, //Event only occurs in Normal instance difficulty + EFLAG_HEROIC = 0x04, //Event only occurs in Heroic instance difficulty + EFLAG_RESERVED_3 = 0x08, + EFLAG_RESERVED_4 = 0x10, + EFLAG_RESERVED_5 = 0x20, + EFLAG_RESERVED_6 = 0x40, + EFLAG_DEBUG_ONLY = 0x80, //Event only occurs in debug build of SD2 only +}; + +struct EventAI_Event +{ + uint32 event_id; + + uint32 creature_id; + + uint16 event_type; + uint32 event_inverse_phase_mask; + uint8 event_chance; + uint8 event_flags; + union + { + uint32 event_param1; + int32 event_param1_s; + }; + union + { + uint32 event_param2; + int32 event_param2_s; + }; + union + { + uint32 event_param3; + int32 event_param3_s; + }; + union + { + uint32 event_param4; + int32 event_param4_s; + }; + + struct _action + { + uint16 type; + union + { + uint32 param1; + int32 param1_s; + }; + union + { + uint32 param2; + int32 param2_s; + }; + union + { + uint32 param3; + int32 param3_s; + }; + }action[MAX_ACTIONS]; +}; + +//Event_Map +extern std::list EventAI_Event_List; + +struct EventAI_Summon +{ + uint32 id; + + float position_x; + float position_y; + float position_z; + float orientation; + uint32 SpawnTimeSecs; +}; + +//EventSummon_Map +extern HM_NAMESPACE::hash_map EventAI_Summon_Map; + +//EventAI Error handling +extern uint32 EAI_ErrorLevel; +/* + +struct EventAI_CreatureError +{ + bool ListEmpty; + bool NoInstance; +}; + +//Error prevention list +extern HM_NAMESPACE::hash_map EventAI_CreatureErrorPreventionList; + +//Defines +#define EVENTAI_EMPTY_EVENTLIST "SD2: Eventlist for Creature %i is empty but creature is using Mob_EventAI. Preventing EventAI on this creature." +*/ +#endif diff --git a/src/bindings/scripts/scripts/creature/mob_generic_creature.cpp b/src/bindings/scripts/scripts/creature/mob_generic_creature.cpp new file mode 100644 index 00000000000..6c05ceffc80 --- /dev/null +++ b/src/bindings/scripts/scripts/creature/mob_generic_creature.cpp @@ -0,0 +1,172 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: Generic_Creature +SD%Complete: 80 +SDComment: Should be replaced with core based AI +SDCategory: Creatures +EndScriptData */ + +#include "precompiled.h" + +#define GENERIC_CREATURE_COOLDOWN 5000 + +struct MANGOS_DLL_DECL generic_creatureAI : public ScriptedAI +{ + generic_creatureAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 GlobalCooldown; //This variable acts like the global cooldown that players have (1.5 seconds) + uint32 BuffTimer; //This variable keeps track of buffs + bool IsSelfRooted; + + void Reset() + { + GlobalCooldown = 0; + BuffTimer = 0; //Rebuff as soon as we can + IsSelfRooted = false; + } + + void Aggro(Unit *who) + { + if (!m_creature->IsWithinDistInMap(who, ATTACK_DISTANCE)) + { + IsSelfRooted = true; + } + } + + void UpdateAI(const uint32 diff) + { + //Always decrease our global cooldown first + if (GlobalCooldown > diff) + GlobalCooldown -= diff; + else GlobalCooldown = 0; + + //Buff timer (only buff when we are alive and not in combat + if (!InCombat && m_creature->isAlive()) + if (BuffTimer < diff ) + { + //Find a spell that targets friendly and applies an aura (these are generally buffs) + SpellEntry const *info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_AURA); + + if (info && !GlobalCooldown) + { + //Cast the buff spell + DoCastSpell(m_creature, info); + + //Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + + //Set our timer to 10 minutes before rebuff + BuffTimer = 600000; + }//Try agian in 30 seconds + else BuffTimer = 30000; + }else BuffTimer -= diff; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //If we are within range melee the target + if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + { + //Make sure our attack is ready and we arn't currently casting + if( m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false)) + { + bool Healing = false; + SpellEntry const *info = NULL; + + //Select a healing spell if less than 30% hp + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 30) + info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING); + + //No healing spell available, select a hostile spell + if (info) Healing = true; + else info = SelectSpell(m_creature->getVictim(), -1, -1, SELECT_TARGET_ANY_ENEMY, 0, 0, 0, 0, SELECT_EFFECT_DONTCARE); + + //50% chance if elite or higher, 20% chance if not, to replace our white hit with a spell + if (info && (rand() % (m_creature->GetCreatureInfo()->rank > 1 ? 2 : 5) == 0) && !GlobalCooldown) + { + //Cast the spell + if (Healing)DoCastSpell(m_creature, info); + else DoCastSpell(m_creature->getVictim(), info); + + //Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + } + else m_creature->AttackerStateUpdate(m_creature->getVictim()); + + m_creature->resetAttackTimer(); + } + } + else + { + //Only run this code if we arn't already casting + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + bool Healing = false; + SpellEntry const *info = NULL; + + //Select a healing spell if less than 30% hp ONLY 33% of the time + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 30 && rand() % 3 == 0) + info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING); + + //No healing spell available, See if we can cast a ranged spell (Range must be greater than ATTACK_DISTANCE) + if (info) Healing = true; + else info = SelectSpell(m_creature->getVictim(), -1, -1, SELECT_TARGET_ANY_ENEMY, 0, 0, ATTACK_DISTANCE, 0, SELECT_EFFECT_DONTCARE); + + //Found a spell, check if we arn't on cooldown + if (info && !GlobalCooldown) + { + //If we are currently moving stop us and set the movement generator + if (!IsSelfRooted) + { + IsSelfRooted = true; + } + + //Cast spell + if (Healing) DoCastSpell(m_creature,info); + else DoCastSpell(m_creature->getVictim(),info); + + //Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + + + }//If no spells available and we arn't moving run to target + else if (IsSelfRooted) + { + //Cancel our current spell and then allow movement agian + m_creature->InterruptNonMeleeSpells(false); + IsSelfRooted = false; + } + } + } + } +}; +CreatureAI* GetAI_generic_creature(Creature *_Creature) +{ + return new generic_creatureAI (_Creature); +} + + +void AddSC_generic_creature() +{ + Script *newscript; + newscript = new Script; + newscript->Name="generic_creature"; + newscript->GetAI = GetAI_generic_creature; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/creature/simple_ai.cpp b/src/bindings/scripts/scripts/creature/simple_ai.cpp new file mode 100644 index 00000000000..e93bfba6425 --- /dev/null +++ b/src/bindings/scripts/scripts/creature/simple_ai.cpp @@ -0,0 +1,294 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: SimpleAI +SD%Complete: 100 +SDComment: Base Class for SimpleAI creatures +SDCategory: Creatures +EndScriptData */ + +#include "precompiled.h" +#include "simple_ai.h" + +SimpleAI::SimpleAI(Creature *c) : ScriptedAI(c) +{ + //Clear all data + Aggro_Text[0] = NULL; + Aggro_Text[1] = NULL; + Aggro_Text[2] = NULL; + Aggro_Say[0] = false; + Aggro_Say[1] = false; + Aggro_Say[2] = false; + Aggro_Sound[0] = 0; + Aggro_Sound[1] = 0; + Aggro_Sound[2] = 0; + + Death_Text[0] = NULL; + Death_Text[1] = NULL; + Death_Text[2] = NULL; + Death_Say[0] = false; + Death_Say[1] = false; + Death_Say[2] = false; + Death_Sound[0] = 0; + Death_Sound[1] = 0; + Death_Sound[2] = 0; + Death_Spell = 0; + Death_Target_Type = 0; + + Kill_Text[0] = NULL; + Kill_Text[1] = NULL; + Kill_Text[2] = NULL; + Kill_Say[0] = false; + Kill_Say[1] = false; + Kill_Say[2] = false; + Kill_Sound[0] = 0; + Kill_Sound[1] = 0; + Kill_Sound[2] = 0; + Kill_Spell = 0; + Kill_Target_Type = 0; + + memset(Spell,0,sizeof(Spell)); + + EnterEvadeMode(); +} + +void SimpleAI::Reset() +{ +} + +void SimpleAI::Aggro(Unit *who) +{ + //Reset cast timers + if (Spell[0].First_Cast >= 0) + Spell_Timer[0] = Spell[0].First_Cast; + else Spell_Timer[0] = 1000; + if (Spell[1].First_Cast >= 0) + Spell_Timer[1] = Spell[1].First_Cast; + else Spell_Timer[1] = 1000; + if (Spell[2].First_Cast >= 0) + Spell_Timer[2] = Spell[2].First_Cast; + else Spell_Timer[2] = 1000; + if (Spell[3].First_Cast >= 0) + Spell_Timer[3] = Spell[3].First_Cast; + else Spell_Timer[3] = 1000; + if (Spell[4].First_Cast >= 0) + Spell_Timer[4] = Spell[4].First_Cast; + else Spell_Timer[4] = 1000; + if (Spell[5].First_Cast >= 0) + Spell_Timer[5] = Spell[5].First_Cast; + else Spell_Timer[5] = 1000; + if (Spell[6].First_Cast >= 0) + Spell_Timer[6] = Spell[6].First_Cast; + else Spell_Timer[6] = 1000; + if (Spell[7].First_Cast >= 0) + Spell_Timer[7] = Spell[7].First_Cast; + else Spell_Timer[7] = 1000; + if (Spell[8].First_Cast >= 0) + Spell_Timer[8] = Spell[8].First_Cast; + else Spell_Timer[8] = 1000; + if (Spell[9].First_Cast >= 0) + Spell_Timer[9] = Spell[9].First_Cast; + else Spell_Timer[9] = 1000; + + uint32 random_text = rand()%3; + + //Random yell + if (Aggro_Text[random_text]) + if (Aggro_Say[random_text]) + DoSay(Aggro_Text[random_text], LANG_UNIVERSAL, who); + else DoYell(Aggro_Text[random_text], LANG_UNIVERSAL, who); + + //Random sound + if (Aggro_Sound[random_text]) + DoPlaySoundToSet(m_creature, Aggro_Sound[random_text]); +} + +void SimpleAI::KilledUnit(Unit *victim) +{ + uint32 random_text = rand()%3; + + //Random yell + if (Kill_Text[random_text]) + if (Kill_Say[random_text]) + DoSay(Kill_Text[random_text], LANG_UNIVERSAL, victim); + else DoYell(Kill_Text[random_text], LANG_UNIVERSAL, victim); + + //Random sound + if (Kill_Sound[random_text]) + DoPlaySoundToSet(m_creature, Kill_Sound[random_text]); + + if (!Kill_Spell) + return; + + Unit* target = NULL; + + switch (Kill_Target_Type) + { + case CAST_SELF: + target = m_creature; + break; + case CAST_HOSTILE_TARGET: + target = m_creature->getVictim(); + break; + case CAST_HOSTILE_SECOND_AGGRO: + target = SelectUnit(SELECT_TARGET_TOPAGGRO,1); + break; + case CAST_HOSTILE_LAST_AGGRO: + target = SelectUnit(SELECT_TARGET_BOTTOMAGGRO,0); + break; + case CAST_HOSTILE_RANDOM: + target = SelectUnit(SELECT_TARGET_RANDOM,0); + break; + case CAST_KILLEDUNIT_VICTIM: + target = victim; + break; + } + + //Target is ok, cast a spell on it + if (target) + DoCast(target, Kill_Spell); +} + +void SimpleAI::DamageTaken(Unit *killer, uint32 &damage) +{ + //Return if damage taken won't kill us + if (m_creature->GetHealth() > damage) + return; + + uint32 random_text = rand()%3; + + //Random yell + if (Death_Text[random_text]) + if (Death_Say[random_text]) + DoSay(Death_Text[random_text], LANG_UNIVERSAL, killer); + else DoYell(Death_Text[random_text], LANG_UNIVERSAL, killer); + + //Random sound + if (Death_Sound[random_text]) + DoPlaySoundToSet(m_creature, Death_Sound[random_text]); + + if (!Death_Spell) + return; + + Unit* target = NULL; + + switch (Death_Target_Type) + { + case CAST_SELF: + target = m_creature; + break; + case CAST_HOSTILE_TARGET: + target = m_creature->getVictim(); + break; + case CAST_HOSTILE_SECOND_AGGRO: + target = SelectUnit(SELECT_TARGET_TOPAGGRO,1); + break; + case CAST_HOSTILE_LAST_AGGRO: + target = SelectUnit(SELECT_TARGET_BOTTOMAGGRO,0); + break; + case CAST_HOSTILE_RANDOM: + target = SelectUnit(SELECT_TARGET_RANDOM,0); + break; + case CAST_JUSTDIED_KILLER: + target = killer; + break; + } + + //Target is ok, cast a spell on it + if (target) + DoCast(target, Death_Spell); +} + +void SimpleAI::UpdateAI(const uint32 diff) +{ + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Spells + for (uint32 i = 0; i < 10; ++i) + { + //Spell not valid + if (!Spell[i].Enabled || !Spell[i].Spell_Id) + continue; + + if (Spell_Timer[i] < diff) + { + //Check if this is a percentage based + if (Spell[i].First_Cast < 0 && Spell[i].First_Cast > -100 && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() > -Spell[i].First_Cast) + continue; + + //Check Current spell + if (!(Spell[i].InterruptPreviousCast && m_creature->IsNonMeleeSpellCasted(false))) + { + Unit* target = NULL; + + switch (Spell[i].Cast_Target_Type) + { + case CAST_SELF: + target = m_creature; + break; + case CAST_HOSTILE_TARGET: + target = m_creature->getVictim(); + break; + case CAST_HOSTILE_SECOND_AGGRO: + target = SelectUnit(SELECT_TARGET_TOPAGGRO,1); + break; + case CAST_HOSTILE_LAST_AGGRO: + target = SelectUnit(SELECT_TARGET_BOTTOMAGGRO,0); + break; + case CAST_HOSTILE_RANDOM: + target = SelectUnit(SELECT_TARGET_RANDOM,0); + break; + } + + //Target is ok, cast a spell on it and then do our random yell + if (target) + { + if (m_creature->IsNonMeleeSpellCasted(false)) + m_creature->InterruptNonMeleeSpells(false); + + DoCast(target, Spell[i].Spell_Id); + + //Yell and sound use the same number so that you can make + //the creature yell with the correct sound effect attached + uint32 random_text = rand()%3; + + //Random yell + if (Spell[i].Text[random_text]) + if (Spell[i].Say[random_text]) + DoSay(Spell[i].Text[random_text], LANG_UNIVERSAL, target); + else DoYell(Spell[i].Text[random_text], LANG_UNIVERSAL, target); + + //Random sound + if (Spell[i].Text_Sound[random_text]) + DoPlaySoundToSet(m_creature, Spell[i].Text_Sound[random_text]); + } + + } + + //Spell will cast agian when the cooldown is up + if (Spell[i].CooldownRandomAddition) + Spell_Timer[i] = Spell[i].Cooldown + (rand() % Spell[i].CooldownRandomAddition); + else Spell_Timer[i] = Spell[i].Cooldown; + + }else Spell_Timer[i] -= diff; + + } + + DoMeleeAttackIfReady(); +} diff --git a/src/bindings/scripts/scripts/creature/simple_ai.h b/src/bindings/scripts/scripts/creature/simple_ai.h new file mode 100644 index 00000000000..0ac78dc1222 --- /dev/null +++ b/src/bindings/scripts/scripts/creature/simple_ai.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_SIMPLEAI_H +#define SC_SIMPLEAI_H + +enum CastTarget +{ + CAST_SELF = 0, //Self cast + CAST_HOSTILE_TARGET, //Our current target (ie: highest aggro) + CAST_HOSTILE_SECOND_AGGRO, //Second highest aggro (generaly used for cleaves and some special attacks) + CAST_HOSTILE_LAST_AGGRO, //Dead last on aggro (no idea what this could be used for) + CAST_HOSTILE_RANDOM, //Just any random target on our threat list + CAST_FRIENDLY_RANDOM, //NOT YET IMPLEMENTED + + //Special cases + CAST_KILLEDUNIT_VICTIM, //Only works within KilledUnit function + CAST_JUSTDIED_KILLER, //Only works within JustDied function +}; + +struct MANGOS_DLL_DECL SimpleAI : public ScriptedAI +{ + SimpleAI(Creature *c);// : ScriptedAI(c); + + void Reset(); + + void Aggro(Unit *who); + + void KilledUnit(Unit *victim); + + void DamageTaken(Unit *killer, uint32 &damage); + + void UpdateAI(const uint32 diff); + +public: + + char* Aggro_Text[3]; + bool Aggro_Say[3]; + uint32 Aggro_Sound[3]; + + char* Death_Text[3]; + bool Death_Say[3]; + uint32 Death_Sound[3]; + uint32 Death_Spell; + uint32 Death_Target_Type; + + char* Kill_Text[3]; + bool Kill_Say[3]; + uint32 Kill_Sound[3]; + uint32 Kill_Spell; + uint32 Kill_Target_Type; + + struct SimpleAI_Spell + { + uint32 Spell_Id; //Spell ID to cast + int32 First_Cast; //Delay for first cast + uint32 Cooldown; //Cooldown between casts + uint32 CooldownRandomAddition; //Random addition to cooldown (in range from 0 - CooldownRandomAddition) + uint32 Cast_Target_Type; //Target type (note that certain spells may ignore this) + bool InterruptPreviousCast; //Interrupt a previous cast if this spell needs to be cast + bool Enabled; //Spell enabled or disabled (default: false) + + //3 texts to many? + char* Text[3]; + bool Say[3]; + uint32 Text_Sound[3]; + }Spell[10]; + +protected: + uint32 Spell_Timer[10]; +}; + +#endif diff --git a/src/bindings/scripts/scripts/custom/custom_example.cpp b/src/bindings/scripts/scripts/custom/custom_example.cpp new file mode 100644 index 00000000000..e6cdd2f8e62 --- /dev/null +++ b/src/bindings/scripts/scripts/custom/custom_example.cpp @@ -0,0 +1,277 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Custom_Example +SD%Complete: 100 +SDComment: Short custom scripting example +SDCategory: Script Examples +EndScriptData */ + +#include "precompiled.h" + +// **** This script is designed as an example for others to build on **** +// **** Please modify whatever you'd like to as this script is only for developement **** + +// **** Script Info **** +// This script is written in a way that it can be used for both friendly and hostile monsters +// Its primary purpose is to show just how much you can really do with scripts +// I recommend trying it out on both an agressive NPC and on friendly npc + +// **** Quick Info **** +// Functions with Handled Function marked above them are functions that are called automatically by the core +// Functions that are marked Custom Function are functions I've created to simplify code + +#define SPELL_BUFF 25661 +#define SPELL_ONE 12555 +#define SPELL_ONE_ALT 24099 +#define SPELL_TWO 10017 +#define SPELL_THREE 26027 +#define SPELL_ENRAGE 23537 +#define SPELL_BESERK 32309 + +#define SAY_AGGRO "Let the games begin." +#define SAY_RANDOM_0 "I see endless suffering. I see torment. I see rage. I see everything." +#define SAY_RANDOM_1 "Muahahahaha" +#define SAY_RANDOM_2 "These mortal infedels my lord, they have invaded your sanctum and seek to steal your secrets." +#define SAY_RANDOM_3 "You are already dead." +#define SAY_RANDOM_4 "Where to go? What to do? So many choices that all end in pain, end in death." +#define SAY_BESERK "$N, I sentance you to death!" +#define SAY_PHASE "The suffering has just begun!" + +#define GOSSIP_ITEM "I'm looking for a fight" +#define SAY_DANCE "I always thought I was a good dancer" +#define SAY_SALUTE "Move out Soldier!" + +struct MANGOS_DLL_DECL custom_exampleAI : public ScriptedAI +{ + //*** HANDLED FUNCTION *** + //This is the constructor, called only once when the creature is first created + custom_exampleAI(Creature *c) : ScriptedAI(c) {Reset();} + + //*** CUSTOM VARIABLES **** + //These variables are for use only by this individual script. + //Nothing else will ever call them but us. + + uint32 Say_Timer; //Timer for random chat + uint32 Rebuff_Timer; //Timer for rebuffing + uint32 Spell_1_Timer; //Timer for spell 1 when in combat + uint32 Spell_2_Timer; //Timer for spell 1 when in combat + uint32 Spell_3_Timer; //Timer for spell 1 when in combat + uint32 Beserk_Timer; //Timer until we go into Beserk (enraged) mode + uint32 Phase; //The current battle phase we are in + uint32 Phase_Timer; //Timer until phase transition + + //*** HANDLED FUNCTION *** + //This is called whenever the core decides we need to evade + void Reset() + { + Phase = 1; //Start in phase 1 + Phase_Timer = 60000; //60 seconds + Spell_1_Timer = 5000; //5 seconds + Spell_2_Timer = 37000; //37 seconds + Spell_3_Timer = 19000; //19 seconds + Beserk_Timer = 120000; //2 minutes + } + + //*** HANDLED FUNCTION *** + //Attack Start is called whenever someone hits us. + void Aggro(Unit *who) + { + //Say some stuff + DoSay(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,8280); + } + + //*** HANDLED FUNCTION *** + //Update AI is called Every single map update (roughly once every 100ms if a player is within the grid) + void UpdateAI(const uint32 diff) + { + //Out of combat timers + if (!m_creature->getVictim()) + { + //Random Say timer + if (Say_Timer < diff) + { + //Random switch between 5 outcomes + switch (rand()%5) + { + case 0: + DoYell(SAY_RANDOM_0,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,8831); //8831 is the index of the sound we are playing. You find these numbers in SoundEntries.dbc + break; + + case 1: + DoYell(SAY_RANDOM_1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,8818); + break; + + case 2: + DoYell(SAY_RANDOM_2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,8041); + break; + + case 3: + DoYell(SAY_RANDOM_3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,8581); + break; + + case 4: + DoYell(SAY_RANDOM_4,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,8791); + break; + } + + Say_Timer = 45000; //Say something agian in 45 seconds + }else Say_Timer -= diff; + + //Rebuff timer + if (Rebuff_Timer < diff) + { + DoCast(m_creature,SPELL_BUFF); + Rebuff_Timer = 900000; //Rebuff agian in 15 minutes + }else Rebuff_Timer -= diff; + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Spell 1 timer + if (Spell_1_Timer < diff) + { + //Cast spell one on our current target. + if (rand()%50 > 10) + DoCast(m_creature->getVictim(),SPELL_ONE_ALT); + else if (m_creature->GetDistance(m_creature->getVictim()) < 25) + DoCast(m_creature->getVictim(),SPELL_ONE); + + Spell_1_Timer = 5000; + }else Spell_1_Timer -= diff; + + //Spell 2 timer + if (Spell_2_Timer < diff) + { + //Cast spell one on our current target. + DoCast(m_creature->getVictim(),SPELL_TWO); + + Spell_2_Timer = 37000; + }else Spell_2_Timer -= diff; + + //Spell 3 timer + if (Phase > 1) + if (Spell_3_Timer < diff) + { + //Cast spell one on our current target. + DoCast(m_creature->getVictim(),SPELL_THREE); + + Spell_3_Timer = 19000; + }else Spell_3_Timer -= diff; + + //Beserk timer + if (Phase > 1) + if (Beserk_Timer < diff) + { + //Say our line then cast uber death spell + DoPlaySoundToSet(m_creature,8588); + DoYell(SAY_BESERK,LANG_UNIVERSAL,m_creature->getVictim()); + DoCast(m_creature->getVictim(),SPELL_BESERK); + + //Cast our beserk spell agian in 12 seconds if we didn't kill everyone + Beserk_Timer = 12000; + }else Beserk_Timer -= diff; + + //Phase timer + if (Phase == 1) + if (Phase_Timer < diff) + { + //Go to next phase + Phase++; + DoYell(SAY_PHASE,LANG_UNIVERSAL,NULL); + DoCast(m_creature,SPELL_ENRAGE); + }else Phase_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//This is the GetAI method used by all scripts that involve AI +//It is called every time a new creature using this script is created +CreatureAI* GetAI_custom_example(Creature *_Creature) +{ + return new custom_exampleAI (_Creature); +} + +//This function is called when the player clicks an option on the gossip menu +void SendDefaultMenu_custom_example(Player *player, Creature *_Creature, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) //Fight time + { + //Set our faction to hostile twoards all + _Creature->setFaction(24); + _Creature->Attack(player, true); + player->PlayerTalkClass->CloseGossip(); + } +} + +//This function is called when the player clicks an option on the gossip menu +bool GossipSelect_custom_example(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (sender == GOSSIP_SENDER_MAIN) + SendDefaultMenu_custom_example(player, _Creature, action); + + return true; +} + +//This function is called when the player opens the gossip menu +bool GossipHello_custom_example(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->PlayerTalkClass->SendGossipMenu(907,_Creature->GetGUID()); + + return true; +} + +//Our Recive emote function +bool ReceiveEmote_custom_example(Player *player, Creature *_Creature, uint32 emote) +{ + _Creature->HandleEmoteCommand(emote); + + if (emote == TEXTEMOTE_DANCE) + ((custom_exampleAI*)_Creature->AI())->DoSay(SAY_DANCE,LANG_UNIVERSAL,NULL); + + if (emote == TEXTEMOTE_SALUTE) + ((custom_exampleAI*)_Creature->AI())->DoSay(SAY_SALUTE,LANG_UNIVERSAL,NULL); + + return true; +} + +//This is the actual function called only once durring InitScripts() +//It must define all handled functions that are to be run in this script +//For example if you want this Script to handle Emotes you must include +//newscript->ReciveEmote = My_Emote_Function; +void AddSC_custom_example() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="custom_example"; + newscript->GetAI = GetAI_custom_example; + newscript->pGossipHello = &GossipHello_custom_example; + newscript->pGossipSelect = &GossipSelect_custom_example; + newscript->pReceiveEmote = &ReceiveEmote_custom_example; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/custom/custom_gossip_codebox.cpp b/src/bindings/scripts/scripts/custom/custom_gossip_codebox.cpp new file mode 100644 index 00000000000..a61871a71c3 --- /dev/null +++ b/src/bindings/scripts/scripts/custom/custom_gossip_codebox.cpp @@ -0,0 +1,81 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Custom_Gossip_Codebox +SD%Complete: 100 +SDComment: Show a codebox in gossip option +SDCategory: Script Examples +EndScriptData */ + +#include "precompiled.h" +#include + +//This function is called when the player opens the gossip menubool +bool GossipHello_custom_gossip_codebox(Player *player, Creature *_Creature) +{ + player->PlayerTalkClass->GetGossipMenu()->AddMenuItem(0, "A quiz: what's your name?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1, "", 0, true); + player->ADD_GOSSIP_ITEM(0, "I'm not interested", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + + player->PlayerTalkClass->SendGossipMenu(907,_Creature->GetGUID()); + return true; +} + +//This function is called when the player clicks an option on the gossip menubool +bool GossipSelect_custom_gossip_codebox(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if(action == GOSSIP_ACTION_INFO_DEF+2) + { + _Creature->Say("Normal select, guess you're not interested.", LANG_UNIVERSAL, 0); + player->CLOSE_GOSSIP_MENU(); + } + return true; +} + +bool GossipSelectWithCode_custom_gossip_codebox( Player *player, Creature *_Creature, uint32 sender, uint32 action, const char* sCode ) +{ + if(sender == GOSSIP_SENDER_MAIN) + { + if(action == GOSSIP_ACTION_INFO_DEF+1) + { + if(std::strcmp(sCode, player->GetName())!=0) + { + _Creature->Say("Wrong!", LANG_UNIVERSAL, 0); + _Creature->CastSpell(player, 12826, true); + } + else + { + _Creature->Say("You're right, you are allowed to see my inner secrets.", LANG_UNIVERSAL, 0); + _Creature->CastSpell(player, 26990, true); + } + player->CLOSE_GOSSIP_MENU(); + return true; + } + } + return false; +} + +void AddSC_custom_gossip_codebox() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="custom_gossip_codebox"; + newscript->pGossipHello = &GossipHello_custom_gossip_codebox; + newscript->pGossipSelect = &GossipSelect_custom_gossip_codebox; + newscript->pGossipSelectWithCode = &GossipSelectWithCode_custom_gossip_codebox; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/custom/test.cpp b/src/bindings/scripts/scripts/custom/test.cpp new file mode 100644 index 00000000000..97876ca0c90 --- /dev/null +++ b/src/bindings/scripts/scripts/custom/test.cpp @@ -0,0 +1,202 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Test +SD%Complete: 100 +SDComment: Script used for testing escortAI +SDCategory: Script Examples +EndScriptData */ + +#include "precompiled.h" +#include "../npc/npc_escortAI.h" + +struct MANGOS_DLL_DECL npc_testAI : public npc_escortAI +{ + public: + + // CreatureAI functions + npc_testAI(Creature *c) : npc_escortAI(c) {Reset();} + + uint32 DeathCoilTimer; + uint32 ChatTimer; + + // Pure Virtual Functions + void WaypointReached(uint32 i) + { + switch (i) + { + case 1: + m_creature->Say("Hmm a nice day for a walk alright", LANG_UNIVERSAL, 0); + break; + + case 3: + { + m_creature->Say("Wild Felboar attack!", LANG_UNIVERSAL, 0); + Creature* temp = m_creature->SummonCreature(21878, m_creature->GetPositionX()+5, m_creature->GetPositionY()+7, m_creature->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 3000); + + temp->AI()->AttackStart(m_creature); + } + break; + + case 4: + { + m_creature->Say("Time for me to go! See ya around $N!", LANG_UNIVERSAL, PlayerGUID); + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_WAVE); + + Unit* temp = Unit::GetUnit(*m_creature, PlayerGUID); + if (temp) + { + temp->MonsterSay("Bye Bye!", LANG_UNIVERSAL, 0); + temp->HandleEmoteCommand(EMOTE_ONESHOT_WAVE); + } + } + break; + } + } + + void Aggro(Unit*) + { + if (IsBeingEscorted) + m_creature->Say("Help $N! I'm under attack!", LANG_UNIVERSAL, PlayerGUID); + else m_creature->Say("Die scum!", LANG_UNIVERSAL, 0); + } + + void Reset() + { + DeathCoilTimer = 4000; + ChatTimer = 4000; + } + + void JustDied(Unit* killer) + { + if (IsBeingEscorted) + { + //killer = m_creature when player got to far from creature + if (killer == m_creature) + { + Unit *pTemp = Unit::GetUnit(*m_creature,PlayerGUID); + if( pTemp ) + DoWhisper("How dare you leave me like that! I hate you! =*(", pTemp); + } + else m_creature->Say("...no...how could you let me die $N", LANG_UNIVERSAL, PlayerGUID); + } + else m_creature->Say("ugh...", LANG_UNIVERSAL, 0); + } + + void UpdateAI(const uint32 diff) + { + //Must update npc_escortAI + npc_escortAI::UpdateAI(diff); + + //Combat check + if (InCombat && m_creature->getVictim()) + { + if (DeathCoilTimer < diff) + { + m_creature->Say("Taste death!", LANG_UNIVERSAL, 0); + m_creature->CastSpell(m_creature->getVictim(), 33130, false); + + DeathCoilTimer = 4000; + }else DeathCoilTimer -= diff; + }else + { + //Out of combat but being escorted + if (IsBeingEscorted) + if (ChatTimer < diff) + { + if (m_creature->HasAura(3593, 0)) + { + m_creature->Say("Fireworks!", LANG_UNIVERSAL, 0); + m_creature->CastSpell(m_creature, 11540, false); + }else + { + m_creature->Say("Hmm, I think I could use a buff", LANG_UNIVERSAL, 0); + m_creature->CastSpell(m_creature, 3593, false); + } + + ChatTimer = 12000; + }else ChatTimer -= diff; + } + } +}; + +CreatureAI* GetAI_test(Creature *_Creature) +{ + npc_testAI* testAI = new npc_testAI(_Creature); + + testAI->AddWaypoint(0, 1231, -4419, 23); + testAI->AddWaypoint(1, 1198, -4440, 23, 0); + testAI->AddWaypoint(2, 1208, -4392, 23); + testAI->AddWaypoint(3, 1231, -4419, 23, 5000); + testAI->AddWaypoint(4, 1208, -4392, 23, 5000); + + return (CreatureAI*)testAI; +} + +bool GossipHello_npc_test(Player *player, Creature *_Creature) +{ + player->TalkedToCreature(_Creature->GetEntry(),_Creature->GetGUID()); + _Creature->prepareGossipMenu(player,0); + + player->PlayerTalkClass->GetGossipMenu()->AddMenuItem(0, "Click to Test Escort(Attack, Defend, Run)", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1,"",0); + + player->PlayerTalkClass->GetGossipMenu()->AddMenuItem(0, "Click to Test Escort(NoAttack, NoDefend, Walk)", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2,"",0); + + player->PlayerTalkClass->GetGossipMenu()->AddMenuItem(0, "Click to Test Escort(NoAttack, Defend, Walk)", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3,"",0); + + _Creature->sendPreparedGossip( player ); + return true; +} + +bool GossipSelect_npc_test(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + player->CLOSE_GOSSIP_MENU(); + ((npc_escortAI*)(_Creature->AI()))->Start(true, true, true, player->GetGUID()); + + return true; // prevent mangos core handling + } + + if (action == GOSSIP_ACTION_INFO_DEF+2) + { + player->CLOSE_GOSSIP_MENU(); + ((npc_escortAI*)(_Creature->AI()))->Start(false, false, false, player->GetGUID()); + + return true; // prevent mangos core handling + } + + if (action == GOSSIP_ACTION_INFO_DEF+3) + { + player->CLOSE_GOSSIP_MENU(); + ((npc_escortAI*)(_Creature->AI()))->Start(false, true, false, player->GetGUID()); + + return true; // prevent mangos core handling + } + return false; +} + +void AddSC_test() +{ + Script *newscript; + newscript = new Script; + newscript->Name="test"; + newscript->GetAI = GetAI_test; + newscript->pGossipHello = &GossipHello_npc_test; + newscript->pGossipSelect = &GossipSelect_npc_test; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/go/go_scripts.cpp b/src/bindings/scripts/scripts/go/go_scripts.cpp new file mode 100644 index 00000000000..158f7fbca36 --- /dev/null +++ b/src/bindings/scripts/scripts/go/go_scripts.cpp @@ -0,0 +1,209 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: GO_Scripts +SD%Complete: 100 +SDComment: Quest support: 4285,4287,4288(crystal pylons), 4296. Field_Repair_Bot->Teaches spell 22704. Barov_journal->Teaches spell 26089 +SDCategory: Game Objects +EndScriptData */ + +/* ContentData +go_northern_crystal_pylon +go_eastern_crystal_pylon +go_western_crystal_pylon +go_barov_journal +go_field_repair_bot_74A +go_orb_of_command +go_tablet_of_madness +go_tablet_of_the_seven +go_teleporter +EndContentData */ + +#include "precompiled.h" + +/*###### +## go_crystal_pylons (3x) +######*/ + +bool GOHello_go_northern_crystal_pylon(Player *player, GameObject* _GO) +{ + if (_GO->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) + { + player->PrepareQuestMenu(_GO->GetGUID()); + player->SendPreparedQuest(_GO->GetGUID()); + } + + if (player->GetQuestStatus(4285) == QUEST_STATUS_INCOMPLETE) + player->AreaExploredOrEventHappens(4285); + + return true; +} + +bool GOHello_go_eastern_crystal_pylon(Player *player, GameObject* _GO) +{ + if (_GO->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) + { + player->PrepareQuestMenu(_GO->GetGUID()); + player->SendPreparedQuest(_GO->GetGUID()); + } + + if (player->GetQuestStatus(4287) == QUEST_STATUS_INCOMPLETE) + player->AreaExploredOrEventHappens(4287); + + return true; +} + +bool GOHello_go_western_crystal_pylon(Player *player, GameObject* _GO) +{ + if (_GO->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) + { + player->PrepareQuestMenu(_GO->GetGUID()); + player->SendPreparedQuest(_GO->GetGUID()); + } + + if (player->GetQuestStatus(4288) == QUEST_STATUS_INCOMPLETE) + player->AreaExploredOrEventHappens(4288); + + return true; +} + +/*###### +## go_barov_journal +######*/ + +bool GOHello_go_barov_journal(Player *player, GameObject* _GO) +{ + if(player->HasSkill(SKILL_TAILORING) && player->GetBaseSkillValue(SKILL_TAILORING) >= 280 && !player->HasSpell(26086)) + { + player->CastSpell(player,26095,false); + } + return true; +} + +/*###### +## go_field_repair_bot_74A +######*/ + +bool GOHello_go_field_repair_bot_74A(Player *player, GameObject* _GO) +{ + if(player->HasSkill(SKILL_ENGINERING) && player->GetBaseSkillValue(SKILL_ENGINERING) >= 300 && !player->HasSpell(22704)) + { + player->CastSpell(player,22864,false); + } + return true; +} + +/*###### +## go_orb_of_command +######*/ + +bool GOHello_go_orb_of_command(Player *player, GameObject* _GO) +{ + if( player->GetQuestRewardStatus(7761) ) + player->CastSpell(player,23460,true); + + return true; +} + +/*###### +## go_tablet_of_madness +######*/ + +bool GOHello_go_tablet_of_madness(Player *player, GameObject* _GO) +{ + if (player->HasSkill(SKILL_ALCHEMY) && player->GetSkillValue(SKILL_ALCHEMY) >= 300 && !player->HasSpell(24266)) + { + player->CastSpell(player,24267,false); + } + return true; +} + +/*###### +## go_tablet_of_the_seven +######*/ + +//TODO: use gossip option ("Transcript the Tablet") instead, if Mangos adds support. +bool GOHello_go_tablet_of_the_seven(Player *player, GameObject* _GO) +{ + if (_GO->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER) + return true; + + if (player->GetQuestStatus(4296) == QUEST_STATUS_INCOMPLETE) + player->CastSpell(player,15065,false); + + return true; +} + +/*###### +## go_teleporter +######*/ + +bool GOHello_go_teleporter(Player *player, GameObject* _GO) +{ + player->TeleportTo(0, 1807.07f,336.105f,70.3975f,0.0f); + return false; +} + +void AddSC_go_scripts() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="go_northern_crystal_pylon"; + newscript->pGOHello = &GOHello_go_northern_crystal_pylon; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="go_eastern_crystal_pylon"; + newscript->pGOHello = &GOHello_go_eastern_crystal_pylon; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="go_western_crystal_pylon"; + newscript->pGOHello = &GOHello_go_western_crystal_pylon; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="go_barov_journal"; + newscript->pGOHello = &GOHello_go_barov_journal; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="go_field_repair_bot_74A"; + newscript->pGOHello = &GOHello_go_field_repair_bot_74A; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="go_orb_of_command"; + newscript->pGOHello = &GOHello_go_orb_of_command; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="go_tablet_of_madness"; + newscript->pGOHello = GOHello_go_tablet_of_madness; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="go_tablet_of_the_seven"; + newscript->pGOHello = GOHello_go_tablet_of_the_seven; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="go_teleporter"; + newscript->pGOHello = GOHello_go_teleporter; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/guard/guard_ai.cpp b/src/bindings/scripts/scripts/guard/guard_ai.cpp new file mode 100644 index 00000000000..7a9d1210bf1 --- /dev/null +++ b/src/bindings/scripts/scripts/guard/guard_ai.cpp @@ -0,0 +1,160 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Guard_AI +SD%Complete: 90 +SDComment: +SDCategory: Guards +EndScriptData */ + +#include "precompiled.h" +#include "guard_ai.h" + +// **** This script is for use within every single guard to save coding time **** + +#define GENERIC_CREATURE_COOLDOWN 5000 + +void guardAI::Reset() +{ + GlobalCooldown = 0; + BuffTimer = 0; //Rebuff as soon as we can +} + +void guardAI::Aggro(Unit *who) +{ +} + +void guardAI::JustDied(Unit *Killer) +{ + //Send Zone Under Attack message to the LocalDefense and WorldDefense Channels + if( Killer->GetTypeId() == TYPEID_PLAYER ) + m_creature->SendZoneUnderAttackMessage((Player*)Killer); + else if( Unit *owner = Killer->GetOwner() ) + { + if( owner->GetTypeId() == TYPEID_PLAYER ) + m_creature->SendZoneUnderAttackMessage((Player*)owner); + } +} + +void guardAI::UpdateAI(const uint32 diff) +{ + //Always decrease our global cooldown first + if (GlobalCooldown > diff) + GlobalCooldown -= diff; + else GlobalCooldown = 0; + + //Buff timer (only buff when we are alive and not in combat + if (m_creature->isAlive() && !InCombat) + if (BuffTimer < diff ) + { + //Find a spell that targets friendly and applies an aura (these are generally buffs) + SpellEntry const *info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_AURA); + + if (info && !GlobalCooldown) + { + //Cast the buff spell + DoCastSpell(m_creature, info); + + //Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + + //Set our timer to 10 minutes before rebuff + BuffTimer = 600000; + } //Try agian in 30 seconds + else BuffTimer = 30000; + }else BuffTimer -= diff; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + // Make sure our attack is ready and we arn't currently casting + 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)) + { + bool Healing = false; + SpellEntry const *info = NULL; + + //Select a healing spell if less than 30% hp + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 30) + info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING); + + //No healing spell available, select a hostile spell + if (info) Healing = true; + else info = SelectSpell(m_creature->getVictim(), -1, -1, SELECT_TARGET_ANY_ENEMY, 0, 0, 0, 0, SELECT_EFFECT_DONTCARE); + + //20% chance to replace our white hit with a spell + if (info && rand() % 5 == 0 && !GlobalCooldown) + { + //Cast the spell + if (Healing)DoCastSpell(m_creature, info); + else DoCastSpell(m_creature->getVictim(), info); + + //Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + } + else m_creature->AttackerStateUpdate(m_creature->getVictim()); + + m_creature->resetAttackTimer(); + } + } + else + { + //Only run this code if we arn't already casting + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + bool Healing = false; + SpellEntry const *info = NULL; + + //Select a healing spell if less than 30% hp ONLY 33% of the time + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 30 && rand() % 3 == 0) + info = SelectSpell(m_creature, -1, -1, SELECT_TARGET_ANY_FRIEND, 0, 0, 0, 0, SELECT_EFFECT_HEALING); + + //No healing spell available, See if we can cast a ranged spell (Range must be greater than ATTACK_DISTANCE) + if (info) Healing = true; + else info = SelectSpell(m_creature->getVictim(), -1, -1, SELECT_TARGET_ANY_ENEMY, 0, 0, ATTACK_DISTANCE, 0, SELECT_EFFECT_DONTCARE); + + //Found a spell, check if we arn't on cooldown + if (info && !GlobalCooldown) + { + //If we are currently moving stop us and set the movement generator + if ((*m_creature).GetMotionMaster()->GetCurrentMovementGeneratorType()!=IDLE_MOTION_TYPE) + { + (*m_creature).GetMotionMaster()->Clear(false); + (*m_creature).GetMotionMaster()->MoveIdle(); + } + + //Cast spell + if (Healing) DoCastSpell(m_creature,info); + else DoCastSpell(m_creature->getVictim(),info); + + //Set our global cooldown + GlobalCooldown = GENERIC_CREATURE_COOLDOWN; + + } //If no spells available and we arn't moving run to target + else if ((*m_creature).GetMotionMaster()->GetCurrentMovementGeneratorType()!=TARGETED_MOTION_TYPE) + { + //Cancel our current spell and then mutate new movement generator + m_creature->InterruptNonMeleeSpells(false); + (*m_creature).GetMotionMaster()->Clear(false); + (*m_creature).GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + } + } +} diff --git a/src/bindings/scripts/scripts/guard/guard_ai.h b/src/bindings/scripts/scripts/guard/guard_ai.h new file mode 100644 index 00000000000..80f88247945 --- /dev/null +++ b/src/bindings/scripts/scripts/guard/guard_ai.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_GUARDAI_H +#define SC_GUARDAI_H + +#define GENERIC_CREATURE_COOLDOWN 5000 + +struct MANGOS_DLL_DECL guardAI : public ScriptedAI +{ + guardAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 GlobalCooldown; //This variable acts like the global cooldown that players have (1.5 seconds) + uint32 BuffTimer; //This variable keeps track of buffs + + void Reset(); + + void Aggro(Unit *who); + + void JustDied(Unit *Killer); + + void UpdateAI(const uint32 diff); +}; +#endif diff --git a/src/bindings/scripts/scripts/guard/guards.cpp b/src/bindings/scripts/scripts/guard/guards.cpp new file mode 100644 index 00000000000..ccd231fc166 --- /dev/null +++ b/src/bindings/scripts/scripts/guard/guards.cpp @@ -0,0 +1,4117 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Guards +SD%Complete: 100 +SDComment: All Guard gossip data, quite some npc_text-id's still missing, adding constantly as new id's are known. CombatAI should be organized better for future. +SDCategory: Guards +EndScriptData */ + +/* ContentData +guard_azuremyst +guard_bluffwatcher +guard_contested +guard_darnassus +guard_dunmorogh +guard_durotar +guard_elwynnforest +guard_eversong +guard_exodar +guard_ironforge +guard_mulgore +guard_orgrimmar +guard_shattrath +guard_shattrath_aldor +guard_shattrath_scryer +guard_silvermoon +guard_stormwind +guard_teldrassil +guard_tirisfal +guard_undercity +EndContentData */ + +#include "precompiled.h" +#include "guard_ai.h" + +//script spesific action +#define GOSSIP_ACTION_TAVERN 101 +#define GOSSIP_ACTION_GEMMERCHANT 102 +#define GOSSIP_ACTION_MANALOOM 103 + +//script spesific sender +#define GOSSIP_SENDER_SEC_GEMMERCHANT 101 +#define GOSSIP_SENDER_SEC_AUCTIONHOUSE 102 + +//script spesific gossip text +#define GOSSIP_TEXT_TAVERN "Worlds End Tavern" +#define GOSSIP_TEXT_BANKSCYERS "Scyers bank" +#define GOSSIP_TEXT_BANKALDOR "Aldor Bank" +#define GOSSIP_TEXT_INNSCYERS "Scyers Inn" +#define GOSSIP_TEXT_INNALDOR "Aldor Inn" +#define GOSSIP_TEXT_STABLESCYERS "Scyers Stable" +#define GOSSIP_TEXT_STABLEALDOR "Aldor Stable" +#define GOSSIP_TEXT_BATTLEMASTERALLIANCE "Alliance Battlemasters" +#define GOSSIP_TEXT_BATTLEMASTERHORDE "Horde Battlemasters" +#define GOSSIP_TEXT_BATTLEMASTERARENA "Arena Battlemasters" +#define GOSSIP_TEXT_MANALOOM "Mana Loom" +#define GOSSIP_TEXT_ALCHEMYLAB "Alchemy Lab" +#define GOSSIP_TEXT_GEMMERCHANT "Gem Merchant" +#define GOSSIP_TEXT_GEMSCYERS "Scyers Gem Merchant" +#define GOSSIP_TEXT_GEMALDOR "Aldor Gem Merchant" + +#define GOSSIP_TEXT_AH_SILVERMOON_1 "Western Auction House" +#define GOSSIP_TEXT_AH_SILVERMOON_2 "Royal Exchange Auction House" + +#define GOSSIP_TEXT_INN_SILVERMOON_1 "Silvermoon City Inn" +#define GOSSIP_TEXT_INN_SILVERMOON_2 "Wayfarer's Rest tavern" + +//common used for guards in main cities +void DoReplyToTextEmote(Creature *_Creature,uint32 em) +{ + switch(em) + { + case TEXTEMOTE_KISS: _Creature->HandleEmoteCommand(EMOTE_ONESHOT_BOW); break; + case TEXTEMOTE_WAVE: _Creature->HandleEmoteCommand(EMOTE_ONESHOT_WAVE); break; + case TEXTEMOTE_SALUTE: _Creature->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); break; + case TEXTEMOTE_SHY: _Creature->HandleEmoteCommand(EMOTE_ONESHOT_FLEX); break; + case TEXTEMOTE_RUDE: + case TEXTEMOTE_CHICKEN: _Creature->HandleEmoteCommand(EMOTE_ONESHOT_POINT); break; + } +} + +/******************************************************* + * guard_azuremyst start + *******************************************************/ + +bool GossipHello_guard_azuremyst(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HIPPOGRYPH , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(10066,_Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_azuremyst(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bank + player->SEND_POI(-3918.95, -11544.7, 6, 6, 0, "Bank"); + player->SEND_GOSSIP_MENU(10067,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Hippogryph Master + player->SEND_POI(-4057.15, -11788.6, 6, 6, 0, "Stephanos"); + player->SEND_GOSSIP_MENU(10071,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Guild master + player->SEND_POI(-4092.43, -11626.6, 6, 6, 0, "Funaam"); + player->SEND_GOSSIP_MENU(10073,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Inn + player->SEND_POI(-4129.43, -12469, 6, 6, 0, "Caregiver Chellan"); + player->SEND_GOSSIP_MENU(10074,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Stable Master + player->SEND_POI(-4146.42, -12492.7, 6, 6, 0, "Esbina"); + player->SEND_GOSSIP_MENU(10075,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DRUID , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PALADIN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SHAMAN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(10076,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_JEWELCRAFTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 13); + player->SEND_GOSSIP_MENU(10087,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_azuremyst(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Druid + player->SEND_POI(-4274.81, -11495.3, 6, 6, 0, "Shalannius"); + player->SEND_GOSSIP_MENU(10077,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Hunter + player->SEND_POI(-4203.65, -12526.5, 6, 6, 0, "Acteon"); + player->SEND_GOSSIP_MENU(10078,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Mage + player->SEND_POI(-4149.62, -12530.1, 6, 6, 0, "Semid"); + player->SEND_GOSSIP_MENU(10081,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Paladin + player->SEND_POI(-4138.98, -12468.5, 6, 6, 0, "Tullas"); + player->SEND_GOSSIP_MENU(10083,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Priest + player->SEND_POI(-4131.66, -12478.6, 6, 6, 0, "Guvan"); + player->SEND_GOSSIP_MENU(10084,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Shaman + player->SEND_POI(-4162.33, -12456.1, 6, 6, 0, "Tuluun"); + player->SEND_GOSSIP_MENU(10085,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Warrior + player->SEND_POI(-4165.05, -12536.4, 6, 6, 0, "Ruada"); + player->SEND_GOSSIP_MENU(10086,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_azuremyst(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(-4191.15, -12470, 6, 6, 0, "Daedal"); + player->SEND_GOSSIP_MENU(10088,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(-4726.29, -12387, 6, 6, 0, "Blacksmith Calypso"); + player->SEND_GOSSIP_MENU(10089,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-4710.87, -12400.6, 6, 6, 0, "'Cookie' McWeaksauce"); + player->SEND_GOSSIP_MENU(10090,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(-3882.85, -11496.7, 6, 6, 0, "Nahogg"); + player->SEND_GOSSIP_MENU(10091,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_POI(-4157.57, -12470.2, 6, 6, 0, "Artificer Daelo"); + player->SEND_GOSSIP_MENU(10092,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(-4199.11, -12469.9, 6, 6, 0, "Anchorite Fateema"); + player->SEND_GOSSIP_MENU(10093,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(-4266.38, -12985.1, 6, 6, 0, "Diktynna"); + player->SEND_GOSSIP_MENU(10094,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Herbalism + player->SEND_GOSSIP_MENU(10095,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Jewelcrafting + player->SEND_POI(-3781.55, -11541.8, 6, 6, 0, "Farii"); + player->SEND_GOSSIP_MENU(10097,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Leatherworking + player->SEND_POI(-3442.68, -12322.2, 6, 6, 0, "Moordo"); + player->SEND_GOSSIP_MENU(10098,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Mining + player->SEND_POI(-4179.89, -12493.1, 6, 6, 0, "Dulvi"); + player->SEND_GOSSIP_MENU(10097,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Skinning + player->SEND_POI(-3431.17, -12316.5, 6, 6, 0, "Gurf"); + player->SEND_GOSSIP_MENU(10098,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 13: //Tailoring + player->SEND_POI(-4711.54, -12386.7, 6, 6, 0, "Erin Kelly"); + player->SEND_GOSSIP_MENU(10099,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_azuremyst(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_azuremyst(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_azuremyst(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_azuremyst(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_azuremyst end + *******************************************************/ + +CreatureAI* GetAI_guard_azuremyst(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_bluffwatcher start + *******************************************************/ + +bool GossipHello_guard_bluffwatcher(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WINDRIDER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WEAPONMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(3543,_Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_bluffwatcher(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bank + player->SEND_POI(-1257.8, 24.14, 6, 6, 0, "Thunder Bluff Bank"); + player->SEND_GOSSIP_MENU(1292,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Wind master + player->SEND_POI(-1196.43, 28.26, 6, 6, 0, "Wind Rider Roost"); + player->SEND_GOSSIP_MENU(1293,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Guild master + player->SEND_POI(-1296.5, 127.57, 6, 6, 0, "Thunder Bluff Civic Information"); + player->SEND_GOSSIP_MENU(1291,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Inn + player->SEND_POI(-1296, 39.7, 6, 6, 0, "Thunder Bluff Inn"); + player->SEND_GOSSIP_MENU(3153,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Mailbox + player->SEND_POI(-1263.59, 44.36, 6, 6, 0, "Thunder Bluff Mailbox"); + player->SEND_GOSSIP_MENU(3154,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Auction House + player->SEND_POI(1381.77, -4371.16, 6, 6, 0, GOSSIP_TEXT_AUCTIONHOUSE); + player->SEND_GOSSIP_MENU(3155,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Weapon master + player->SEND_POI(-1282.31, 89.56, 6, 6, 0, "Ansekhwa"); + player->SEND_GOSSIP_MENU(4520,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Stable master + player->SEND_POI(-1270.19, 48.84, 6, 6, 0, "Bulrug"); + player->SEND_GOSSIP_MENU(5977,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //battlemaster + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALTERACVALLEY , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ARATHIBASIN , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARSONGULCH , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(7527,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DRUID , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SHAMAN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(3542,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(3541,_Creature->GetGUID()); + break; + } +} + +void SendBattleMasterMenu_guard_bluffwatcher(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //AV + player->SEND_POI(-1387.82, -97.55, 6, 6, 0, "Taim Ragetotem"); + player->SEND_GOSSIP_MENU(7522,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //AB + player->SEND_POI(-997, 214.12, 6, 6, 0, "Martin Lindsey"); + player->SEND_GOSSIP_MENU(7648,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //WSG + player->SEND_POI(-1384.94, -75.91, 6, 6, 0, "Kergul Bloodaxe"); + player->SEND_GOSSIP_MENU(7523,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_bluffwatcher(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Druid + player->SEND_POI(-1054.47, -285, 6, 6, 0, "Hall of Elders"); + player->SEND_GOSSIP_MENU(1294,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Hunter + player->SEND_POI(-1416.32, -114.28, 6, 6, 0, "Hunter's Hall"); + player->SEND_GOSSIP_MENU(1295,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Mage + player->SEND_POI(-1061.2, 195.5, 6, 6, 0, "Pools of Vision"); + player->SEND_GOSSIP_MENU(1296,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Priest + player->SEND_POI(-1061.2, 195.5, 6, 6, 0, "Pools of Vision"); + player->SEND_GOSSIP_MENU(1297,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Shaman + player->SEND_POI(-989.54, 278.25, 6, 6, 0, "Hall of Spirits"); + player->SEND_GOSSIP_MENU(1298,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Warrior + player->SEND_POI(-1416.32, -114.28, 6, 6, 0, "Hunter's Hall"); + player->SEND_GOSSIP_MENU(1299,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_bluffwatcher(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(-1085.56, 27.29, 6, 6, 0, "Bena's Alchemy"); + player->SEND_GOSSIP_MENU(1332,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(-1239.75, 104.88, 6, 6, 0, "Karn's Smithy"); + player->SEND_GOSSIP_MENU(1333,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-1214.5, -21.23, 6, 6, 0, "Aska's Kitchen"); + player->SEND_GOSSIP_MENU(1334,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(-1112.65, 48.26, 6, 6, 0, "Dawnstrider Enchanters"); + player->SEND_GOSSIP_MENU(1335,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //First Aid + player->SEND_POI(-996.58, 200.5, 6, 6, 0, "Spiritual Healing"); + player->SEND_GOSSIP_MENU(1336,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Fishing + player->SEND_POI(-1169.35, -68.87, 6, 6, 0, "Mountaintop Bait & Tackle"); + player->SEND_GOSSIP_MENU(1337,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Herbalism + player->SEND_POI(-1137.7, -1.51, 6, 6, 0, "Holistic Herbalism"); + player->SEND_GOSSIP_MENU(1338,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Leatherworking + player->SEND_POI(-1156.22, 66.86, 6, 6, 0, "Thunder Bluff Armorers"); + player->SEND_GOSSIP_MENU(1339,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Mining + player->SEND_POI(-1249.17, 155, 6, 6, 0, "Stonehoof Geology"); + player->SEND_GOSSIP_MENU(1340,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Skinning + player->SEND_POI(-1148.56, 51.18, 6, 6, 0, "Mooranta"); + player->SEND_GOSSIP_MENU(1343,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Tailoring + player->SEND_POI(-1156.22, 66.86, 6, 6, 0, "Thunder Bluff Armorers"); + player->SEND_GOSSIP_MENU(1341,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_bluffwatcher(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_bluffwatcher(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_bluffwatcher(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_bluffwatcher(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_bluffwatcher(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_bluffwatcher end + *******************************************************/ + +CreatureAI* GetAI_guard_bluffwatcher(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_contested start + *******************************************************/ + +struct MANGOS_DLL_DECL guard_contested : public guardAI +{ + guard_contested(Creature *c) : guardAI(c) {} + + void MoveInLineOfSight(Unit *who) + { + if ( who->isAttackingPlayer() ) + { + if(who->GetTypeId() == TYPEID_PLAYER || who->GetOwnerGUID() && GUID_HIPART(who->GetOwnerGUID())==HIGHGUID_PLAYER) + { + m_creature->AddThreat(who, 0.0f); + if(Unit* owner = who->GetOwner()) + m_creature->AddThreat(owner, 0.0f); + + if(!m_creature->isInCombat()) + { + if (m_creature->GetEntry() == 15184) //Cenarion Hold Infantry + { + srand (time(NULL)); + if (rand()%100 <= 30) + { + DoSay("Taste blade, mongrel!", LANG_UNIVERSAL,NULL); + } + else if (rand()%100 > 30 && rand()%100 < 50) + { + DoSay("Please tell me that you didn`t just do what I think you just did. Please tell me that I`m not going to have to hurt you...", LANG_UNIVERSAL,NULL); + } + else if (rand()%100 >= 50) + { + DoSay("As if we don`t have enough problems, you go and create more!", LANG_UNIVERSAL,NULL); + } + } + else + { + SpellEntry const *spell = m_creature->reachWithSpellAttack(who); + DoCastSpell(who, spell); + } + } + DoStartAttackAndMovement(who); + } + } + } +}; +/******************************************************* + * guard_contested end + *******************************************************/ + +CreatureAI* GetAI_guard_contested(Creature *_Creature) +{ + return new guard_contested (_Creature); +} + +/******************************************************* + * guard_darnassus start + *******************************************************/ + +bool GossipHello_guard_darnassus(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HIPPOGRYPH , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WEAPONMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(3016, _Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_darnassus(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Auction house + player->SEND_POI(9861.23, 2334.55, 6, 6, 0, "Darnassus Auction House"); + player->SEND_GOSSIP_MENU(3833, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Bank + player->SEND_POI(9938.45, 2512.35, 6, 6, 0, "Darnassus Bank"); + player->SEND_GOSSIP_MENU(3017, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Wind master + player->SEND_POI(9945.65, 2618.94, 6, 6, 0, "Rut'theran Village"); + player->SEND_GOSSIP_MENU(3018, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Guild master + player->SEND_POI(10076.40, 2199.59, 6, 6, 0, "Darnassus Guild Master"); + player->SEND_GOSSIP_MENU(3019, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Inn + player->SEND_POI(10133.29, 2222.52, 6, 6, 0, "Darnassus Inn"); + player->SEND_GOSSIP_MENU(3020, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Mailbox + player->SEND_POI(9942.17, 2495.48, 6, 6, 0, "Darnassus Mailbox"); + player->SEND_GOSSIP_MENU(3021, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Stable master + player->SEND_POI(10167.20, 2522.66, 6, 6, 0, "Alassin"); + player->SEND_GOSSIP_MENU(5980, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Weapon trainer + player->SEND_POI(9907.11, 2329.70, 6, 6, 0, "Ilyenia Moonfire"); + player->SEND_GOSSIP_MENU(4517, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Battlemaster + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALTERACVALLEY , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ARATHIBASIN , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARSONGULCH , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(7519, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DRUID , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(4264, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->SEND_GOSSIP_MENU(4273, _Creature->GetGUID()); + break; + } +} + +void SendBattleMasterMenu_guard_darnassus(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //AV + player->SEND_POI(9923.61, 2327.43, 6, 6, 0, "Brogun Stoneshield"); + player->SEND_GOSSIP_MENU(7518, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //AB + player->SEND_POI(9977.37, 2324.39, 6, 6, 0, "Keras Wolfheart"); + player->SEND_GOSSIP_MENU(7651, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //WSG + player->SEND_POI(9979.84, 2315.79, 6, 6, 0, "Aethalas"); + player->SEND_GOSSIP_MENU(7482, _Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_darnassus(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Druid + player->SEND_POI(10186, 2570.46, 6, 6, 0, "Darnassus Druid Trainer"); + player->SEND_GOSSIP_MENU(3024, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Hunter + player->SEND_POI(10177.29, 2511.10, 6, 6, 0, "Darnassus Hunter Trainer"); + player->SEND_GOSSIP_MENU(3023, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Priest + player->SEND_POI(9659.12, 2524.88, 6, 6, 0, "Temple of the Moon"); + player->SEND_GOSSIP_MENU(3025, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Rogue + player->SEND_POI(10122, 2599.12, 6, 6, 0, "Darnassus Rogue Trainer"); + player->SEND_GOSSIP_MENU(3026, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Warrior + player->SEND_POI(9951.91, 2280.38, 6, 6, 0, "Warrior's Terrace"); + player->SEND_GOSSIP_MENU(3033, _Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_darnassus(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(10075.90, 2356.76, 6, 6, 0, "Darnassus Alchemy Trainer"); + player->SEND_GOSSIP_MENU(3035, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Cooking + player->SEND_POI(10088.59, 2419.21, 6, 6, 0, "Darnassus Cooking Trainer"); + player->SEND_GOSSIP_MENU(3036, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Enchanting + player->SEND_POI(10146.09, 2313.42, 6, 6, 0, "Darnassus Enchanting Trainer"); + player->SEND_GOSSIP_MENU(3337, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //First Aid + player->SEND_POI(10150.09, 2390.43, 6, 6, 0, "Darnassus First Aid Trainer"); + player->SEND_GOSSIP_MENU(3037, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Fishing + player->SEND_POI(9836.20, 2432.17, 6, 6, 0, "Darnassus Fishing Trainer"); + player->SEND_GOSSIP_MENU(3038, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Herbalism + player->SEND_POI(9757.17, 2430.16, 6, 6, 0, "Darnassus Herbalism Trainer"); + player->SEND_GOSSIP_MENU(3039, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Leatherworking + player->SEND_POI(10086.59, 2255.77, 6, 6, 0, "Darnassus Leatherworking Trainer"); + player->SEND_GOSSIP_MENU(3040, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Skinning + player->SEND_POI(10081.40, 2257.18, 6, 6, 0, "Darnassus Skinning Trainer"); + player->SEND_GOSSIP_MENU(3042, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Tailoring + player->SEND_POI(10079.70, 2268.19, 6, 6, 0, "Darnassus Tailor"); + player->SEND_GOSSIP_MENU(3044, _Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_darnassus(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_darnassus(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_darnassus(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_darnassus(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_darnassus(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_darnassus end + *******************************************************/ + +CreatureAI* GetAI_guard_darnassus(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_dunmorogh start + *******************************************************/ + +bool GossipHello_guard_dunmorogh(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HIPPOGRYPH , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(4287,_Creature->GetGUID()); + + return true; +} + +void SendDefaultMenu_guard_dunmorogh(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bank + player->SEND_GOSSIP_MENU(4288,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Gryphon master + player->SEND_GOSSIP_MENU(4289,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Guild master + player->SEND_GOSSIP_MENU(4290,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Inn + player->SEND_POI(-5582.66, -525.89, 6, 6, 0, "Thunderbrew Distillery"); + player->SEND_GOSSIP_MENU(4291,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Stable Master + player->SEND_POI(-5604, -509.58, 6, 6, 0, "Shelby Stoneflint"); + player->SEND_GOSSIP_MENU(5985,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PALADIN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARLOCK , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(4292,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(4300,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_dunmorogh(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Hunter + player->SEND_POI(-5618.29, -454.25, 6, 6, 0, "Grif Wildheart"); + player->SEND_GOSSIP_MENU(4293,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Mage + player->SEND_POI(-5585.6, -539.99, 6, 6, 0, "Magis Sparkmantle"); + player->SEND_GOSSIP_MENU(4294,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Paladin + player->SEND_POI(-5585.6, -539.99, 6, 6, 0, "Azar Stronghammer"); + player->SEND_GOSSIP_MENU(4295,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Priest + player->SEND_POI(-5591.74, -525.61, 6, 6, 0, "Maxan Anvol"); + player->SEND_GOSSIP_MENU(4296,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Rogue + player->SEND_POI(-5602.75, -542.4, 6, 6, 0, "Hogral Bakkan"); + player->SEND_GOSSIP_MENU(4297,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Warlock + player->SEND_POI(-5641.97, -523.76, 6, 6, 0, "Gimrizz Shadowcog"); + player->SEND_GOSSIP_MENU(4298,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Warrior + player->SEND_POI(-5604.79, -529.38, 6, 6, 0, "Granis Swiftaxe"); + player->SEND_GOSSIP_MENU(4299,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_dunmorogh(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_GOSSIP_MENU(4301,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(-5584.72, -428.41, 6, 6, 0, "Tognus Flintfire"); + player->SEND_GOSSIP_MENU(4302,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-5596.85, -541.43, 6, 6, 0, "Gremlock Pilsnor"); + player->SEND_GOSSIP_MENU(4303,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_GOSSIP_MENU(4304,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_POI(-5531, -666.53, 6, 6, 0, "Bronk Guzzlegear"); + player->SEND_GOSSIP_MENU(4305,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(-5603.67, -523.57, 6, 6, 0, "Thamner Pol"); + player->SEND_GOSSIP_MENU(4306,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(-5199.9, 58.58, 6, 6, 0, "Paxton Ganter"); + player->SEND_GOSSIP_MENU(4307,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Herbalism + player->SEND_GOSSIP_MENU(4308,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Leatherworking + player->SEND_GOSSIP_MENU(4310,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Mining + player->SEND_POI(-5531, -666.53, 6, 6, 0, "Yarr Hamerstone"); + player->SEND_GOSSIP_MENU(4311,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Skinning + player->SEND_GOSSIP_MENU(4312,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Tailoring + player->SEND_GOSSIP_MENU(4313,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_dunmorogh(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_dunmorogh(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_dunmorogh(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_dunmorogh(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_dunmorogh end + *******************************************************/ + +CreatureAI* GetAI_guard_dunmorogh(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_durotar start + *******************************************************/ + +bool GossipHello_guard_durotar(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WINDRIDER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(4037,_Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_durotar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bank + player->SEND_GOSSIP_MENU(4032,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Wind rider + player->SEND_GOSSIP_MENU(4033,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Inn + player->SEND_POI(338.7, -4688.87, 6, 6, 0, "Razor Hill Inn"); + player->SEND_GOSSIP_MENU(4034,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Stable master + player->SEND_POI(330.31, -4710.66, 6, 6, 0, "Shoja'my"); + player->SEND_GOSSIP_MENU(5973,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SHAMAN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARLOCK , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(4035,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(4036,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_durotar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Hunter + player->SEND_POI(276, -4706.72, 6, 6, 0, "Thotar"); + player->SEND_GOSSIP_MENU(4013,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Mage + player->SEND_POI(-839.33, -4935.6, 6, 6, 0, "Un'Thuwa"); + player->SEND_GOSSIP_MENU(4014,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Priest + player->SEND_POI(296.22, -4828.1, 6, 6, 0, "Tai'jin"); + player->SEND_GOSSIP_MENU(4015,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Rogue + player->SEND_POI(265.76, -4709, 6, 6, 0, "Kaplak"); + player->SEND_GOSSIP_MENU(4016,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Shaman + player->SEND_POI(307.79, -4836.97, 6, 6, 0, "Swart"); + player->SEND_GOSSIP_MENU(4017,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Warlock + player->SEND_POI(355.88, -4836.45, 6, 6, 0, "Dhugru Gorelust"); + player->SEND_GOSSIP_MENU(4018,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Warrior + player->SEND_POI(312.3, -4824.66, 6, 6, 0, "Tarshaw Jaggedscar"); + player->SEND_GOSSIP_MENU(4019,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_durotar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(-800.25, -4894.33, 6, 6, 0, "Miao'zan"); + player->SEND_GOSSIP_MENU(4020,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(373.24, -4716.45, 6, 6, 0, "Dwukk"); + player->SEND_GOSSIP_MENU(4021,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_GOSSIP_MENU(4022,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_GOSSIP_MENU(4023,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_POI(368.95, -4723.95, 6, 6, 0, "Mukdrak"); + player->SEND_GOSSIP_MENU(4024,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(327.17, -4825.62, 6, 6, 0, "Rawrk"); + player->SEND_GOSSIP_MENU(4025,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(-1065.48, -4777.43, 6, 6, 0, "Lau'Tiki"); + player->SEND_GOSSIP_MENU(4026,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Herbalism + player->SEND_POI(-836.25, -4896.89, 6, 6, 0, "Mishiki"); + player->SEND_GOSSIP_MENU(4027,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Leatherworking + player->SEND_GOSSIP_MENU(4028,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Mining + player->SEND_POI(366.94, -4705, 6, 6, 0, "Krunn"); + player->SEND_GOSSIP_MENU(4029,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Skinning + player->SEND_GOSSIP_MENU(4030,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Tailoring + player->SEND_GOSSIP_MENU(4031,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_durotar(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_durotar(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_durotar(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_durotar(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_durotar end + *******************************************************/ + +CreatureAI* GetAI_guard_durotar(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_elwynnforest start + *******************************************************/ + +bool GossipHello_guard_elwynnforest(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GRYPHON , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(933,_Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_elwynnforest(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bank + player->SEND_GOSSIP_MENU(4260,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Gryphon master + player->SEND_GOSSIP_MENU(4261,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Guild master + player->SEND_GOSSIP_MENU(4262,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Inn + player->SEND_POI(-9459.34, 42.08, 6, 6, 0, "Lion's Pride Inn"); + player->SEND_GOSSIP_MENU(4263,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Stable Master + player->SEND_POI(-9466.62, 45.87, 6, 6, 0, "Erma"); + player->SEND_GOSSIP_MENU(5983,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DRUID , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PALADIN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARLOCK , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->SEND_GOSSIP_MENU(4264,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(4273,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_elwynnforest(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Druid + player->SEND_GOSSIP_MENU(4265,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Hunter + player->SEND_GOSSIP_MENU(4266,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Mage + player->SEND_POI(-9471.12, 33.44, 6, 6, 0, "Zaldimar Wefhellt"); + player->SEND_GOSSIP_MENU(4268,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Paladin + player->SEND_POI(-9469, 108.05, 6, 6, 0, "Brother Wilhelm"); + player->SEND_GOSSIP_MENU(4269,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Priest + player->SEND_POI(-9461.07, 32.6, 6, 6, 0, "Priestess Josetta"); + player->SEND_GOSSIP_MENU(4267,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Rogue + player->SEND_POI(-9465.13, 13.29, 6, 6, 0, "Keryn Sylvius"); + player->SEND_GOSSIP_MENU(4270,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Warlock + player->SEND_POI(-9473.21, -4.08, 6, 6, 0, "Maximillian Crowe"); + player->SEND_GOSSIP_MENU(4272,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Warrior + player->SEND_POI(-9461.82, 109.50, 6, 6, 0, "Lyria Du Lac"); + player->SEND_GOSSIP_MENU(4271,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_elwynnforest(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(-9057.04, 153.63, 6, 6, 0, "Alchemist Mallory"); + player->SEND_GOSSIP_MENU(4274,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(-9456.58, 87.90, 6, 6, 0, "Smith Argus"); + player->SEND_GOSSIP_MENU(4275,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-9467.54, -3.16, 6, 6, 0, "Tomas"); + player->SEND_GOSSIP_MENU(4276,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_GOSSIP_MENU(4277,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_GOSSIP_MENU(4278,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(-9456.82, 30.49, 6, 6, 0, "Michelle Belle"); + player->SEND_GOSSIP_MENU(4279,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(-9386.54, -118.73, 6, 6, 0, "Lee Brown"); + player->SEND_GOSSIP_MENU(4280,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Herbalism + player->SEND_POI(-9060.70, 149.23, 6, 6, 0, "Herbalist Pomeroy"); + player->SEND_GOSSIP_MENU(4281,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Leatherworking + player->SEND_POI(-9376.12, -75.23, 6, 6, 0, "Adele Fielder"); + player->SEND_GOSSIP_MENU(4282,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Mining + player->SEND_GOSSIP_MENU(4283,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Skinning + player->SEND_POI(-9536.91, -1212.76, 6, 6, 0, "Helene Peltskinner"); + player->SEND_GOSSIP_MENU(4284,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Tailoring + player->SEND_POI(-9376.12, -75.23, 6, 6, 0, "Eldrin"); + player->SEND_GOSSIP_MENU(4285,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_elwynnforest(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_elwynnforest(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_elwynnforest(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_elwynnforest(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_elwynnforest end + *******************************************************/ + +CreatureAI* GetAI_guard_elwynnforest(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_eversong start + *******************************************************/ + +bool GossipHello_guard_eversong(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATHANDLER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(10180,_Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_eversong(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bat Handler + player->SEND_POI(9371.93, -7164.80, 6, 6, 0, "Skymistress Gloaming"); + player->SEND_GOSSIP_MENU(10181,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Guild master + player->SEND_GOSSIP_MENU(10182,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Inn + player->SEND_POI(9483.74, -6844.58, 6, 6, 0, "Delaniel's inn"); + player->SEND_GOSSIP_MENU(10183,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Stable Master + player->SEND_POI(9489.62, -6829.93, 6, 6, 0, "Anathos"); + player->SEND_GOSSIP_MENU(10184,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DRUID , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PALADIN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARLOCK , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(10180,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_JEWELCRAFTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(10180,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_eversong(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Druid + player->SEND_GOSSIP_MENU(10185,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Hunter + player->SEND_POI(9527.44, -6865.25, 6, 6, 0, "Hannovia"); + player->SEND_GOSSIP_MENU(10186,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Mage + player->SEND_POI(9464.24, -6855.52, 6, 6, 0, "Garridel"); + player->SEND_GOSSIP_MENU(10187,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Paladin + player->SEND_POI(9517.61, -6871.04, 6, 6, 0, "Noellene"); + player->SEND_GOSSIP_MENU(10189,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Priest + player->SEND_POI(9467.39, -6845.72, 6, 6, 0, "Ponaris"); + player->SEND_GOSSIP_MENU(10190,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Rogue + player->SEND_POI(9533.67, -6877.39, 6, 6, 0, "Tannaria"); + player->SEND_GOSSIP_MENU(10191,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Warlock + player->SEND_POI(9468.99, -6865.60, 6, 6, 0, "Celoenus"); + player->SEND_GOSSIP_MENU(10192,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_eversong(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(8659.90, -6368.12, 6, 6, 0, "Arcanist Sheynathren"); + player->SEND_GOSSIP_MENU(10193,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(8984.21, -7419.21, 6, 6, 0, "Arathel Sunforge"); + player->SEND_GOSSIP_MENU(10194,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(9494.04, -6881.51, 6, 6, 0, "Quarelestra"); + player->SEND_GOSSIP_MENU(10195,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Engineering + player->SEND_GOSSIP_MENU(10197,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //First Aid + player->SEND_POI(9479.46, -6879.16, 6, 6, 0, "Kanaria"); + player->SEND_GOSSIP_MENU(10198,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Fishing + player->SEND_GOSSIP_MENU(10199,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Herbalism + player->SEND_POI(8678.92, -6329.09, 6, 6, 0, "Botanist Tyniarrel"); + player->SEND_GOSSIP_MENU(10200,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Jewelcrafting + player->SEND_POI(9484.32, -6874.98, 6, 6, 0, "Aleinia"); + player->SEND_GOSSIP_MENU(10203,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Leatherworking + player->SEND_POI(9362.04, -7130.33, 6, 6, 0, "Sathein"); + player->SEND_GOSSIP_MENU(10204,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Mining + player->SEND_GOSSIP_MENU(10205,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Skinning + player->SEND_POI(9362.04, -7130.33, 6, 6, 0, "Mathreyn"); + player->SEND_GOSSIP_MENU(10206,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Tailoring + player->SEND_POI(8680.36, -6327.51, 6, 6, 0, "Sempstress Ambershine"); + player->SEND_GOSSIP_MENU(10207,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_eversong(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_eversong(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_eversong(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_eversong(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_eversong end + *******************************************************/ + +CreatureAI* GetAI_guard_eversong(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_exodar start + *******************************************************/ + +bool GossipHello_guard_exodar(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HIPPOGRYPH , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WEAPONMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(9551, _Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_exodar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Auction house + player->SEND_POI(-4023.6, -11739.3, 6, 6, 0, "Exodar Auction House"); + player->SEND_GOSSIP_MENU(9528, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Bank + player->SEND_POI(-3923.89, -11544.5, 6, 6, 0, "Exodar Bank"); + player->SEND_GOSSIP_MENU(9529, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Guild master + player->SEND_POI(-4092.57, -11626.5, 6, 6, 0, "Exodar Guild Master"); + player->SEND_GOSSIP_MENU(9539, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Hippogryph master + player->SEND_POI(-4060.46, -11787.1, 6, 6, 0, "Exodar Hippogryph Master"); + player->SEND_GOSSIP_MENU(9530, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Inn + player->SEND_POI(-3741.87, -11695.1, 6, 6, 0, "Exodar Inn"); + player->SEND_GOSSIP_MENU(9545, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Mailbox + player->SEND_POI(-3972.5, -11696.0, 6, 6, 0, "Mailbox"); + player->SEND_GOSSIP_MENU(10254, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Stable master + player->SEND_POI(-3786.5, -11702.5, 6, 6, 0, "Stable Master Arthaid"); + player->SEND_GOSSIP_MENU(9558, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Weapon trainer + player->SEND_POI(-4215.68, -11628.9, 6, 6, 0, "Weapon Master Handiir"); + player->SEND_GOSSIP_MENU(9565, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Battlemaster + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALTERACVALLEY , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ARATHIBASIN , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ARENA , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_EYEOFTHESTORM , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARSONGULCH , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(9531, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DRUID , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PALADIN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SHAMAN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(9533, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_JEWELCRAFTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 13); + player->SEND_GOSSIP_MENU(9555, _Creature->GetGUID()); + break; + } +} + +void SendBattleMasterMenu_guard_exodar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //AV + player->SEND_POI(-3978.1, -11357, 6, 6, 0, "Alterac Valley Battlemaster"); + player->SEND_GOSSIP_MENU(9531, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //AB + player->SEND_POI(-3998.9, -11345.2, 6, 6, 0, "Arathi Basin Battlemaster"); + player->SEND_GOSSIP_MENU(9531, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //A + player->SEND_POI(-3759.27, -11695.63, 6, 6, 0, "Miglik Blotstrom"); + player->SEND_GOSSIP_MENU(10223, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //EOS + player->SEND_POI(-3978.1, -11357, 6, 6, 0, "Eye Of The Storm Battlemaster"); + player->SEND_GOSSIP_MENU(9531, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //WSG + player->SEND_POI(-3977.5, -11381.2, 6, 6, 0, "Warsong Gulch Battlemaster"); + player->SEND_GOSSIP_MENU(9531, _Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_exodar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Druid + player->SEND_POI(-4276.0, -11495, 6, 6, 0, "Exodar Druid Trainer"); + player->SEND_GOSSIP_MENU(9534, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Hunter + player->SEND_POI(-4210.6, -11575.2, 6, 6, 0, "Exodar Hunter Trainer"); + player->SEND_GOSSIP_MENU(9544, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Mage + player->SEND_POI(-4057.32, -11556.5, 6, 6, 0, "Exodar Mage Trainer"); + player->SEND_GOSSIP_MENU(9550, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Paladin + player->SEND_POI(-4191.2, -11470.4, 6, 6, 0, "Exodar Paladin Trainer"); + player->SEND_GOSSIP_MENU(9553, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Priest + player->SEND_POI(-3969.63, -11482.8, 6, 6, 0, "Exodar Priest Trainer"); + player->SEND_GOSSIP_MENU(9554, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Shaman + player->SEND_POI(-3805.5, -11380.7, 6, 6, 0, "Exodar Shaman Trainer"); + player->SEND_GOSSIP_MENU(9556, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Warrior + player->SEND_POI(-4189.43, -11653.7, 6, 6, 0, "Exodar Warrior Trainer"); + player->SEND_GOSSIP_MENU(9562, _Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_exodar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(-4040.6, -11364.5, 6, 6, 0, "Exodar Alchemy Trainer"); + player->SEND_GOSSIP_MENU(9527, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(-4229.5, -11706, 6, 6, 0, "Exodar Blacksmithing Trainer"); + player->SEND_GOSSIP_MENU(9532, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-3798.3, -11651.7, 6, 6, 0, "Exodar Cooking Trainer"); + player->SEND_GOSSIP_MENU(9551, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(-3889.3, -11495, 6, 6, 0, "Exodar Enchanting Trainer"); + player->SEND_GOSSIP_MENU(9535, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_POI(-4257.68, -11640.3, 6, 6, 0, "Exodar Engineering Trainer"); + player->SEND_GOSSIP_MENU(9536, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(-3769.5, -11479.6, 6, 6, 0, "Exodar First Aid Trainer"); + player->SEND_GOSSIP_MENU(9537, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(-3725.5, -11385.2, 6, 6, 0, "Exodar Fishing Trainer"); + player->SEND_GOSSIP_MENU(9538, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Jewelcrafting + player->SEND_POI(-3783, -11546, 6, 6, 0, "Exodar Jewelcrafting Trainer"); + player->SEND_GOSSIP_MENU(9547, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Herbalism + player->SEND_POI(-4040.6, -11364.5, 6, 6, 0, "Exodar Herbalist Trainer"); + player->SEND_GOSSIP_MENU(9543, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Leatherworking + player->SEND_POI(-4140.6, -11776.7, 6, 6, 0, "Exodar Leatherworking Trainer"); + player->SEND_GOSSIP_MENU(9549, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Mining + player->SEND_POI(-4228, -11697, 6, 6, 0, "Exodar Mining Trainer"); + player->SEND_GOSSIP_MENU(9552, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Skinning + player->SEND_POI(-4134.97, -11760.5, 6, 6, 0, "Exodar Skinning Trainer"); + player->SEND_GOSSIP_MENU(9557, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 13: //Tailoring + player->SEND_POI(-4092.5, -11744.5, 6, 6, 0, "Exodar Tailor Trainer"); + player->SEND_GOSSIP_MENU(9559, _Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_exodar(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_exodar(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_exodar(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_exodar(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_exodar(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_exodar end + *******************************************************/ + +CreatureAI* GetAI_guard_exodar(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_ironforge start + *******************************************************/ + +bool GossipHello_guard_ironforge(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_IRONFORGE_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DEEPRUNTRAM , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GRYPHON , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WEAPONMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(2760, _Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_ironforge(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Auction House + player->SEND_POI(-4957.39, -911.6, 6, 6, 0, "Ironforge Auction House"); + player->SEND_GOSSIP_MENU(3014, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Bank + player->SEND_POI(-4891.91, -991.47, 6, 6, 0, "The Vault"); + player->SEND_GOSSIP_MENU(2761, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Tram + player->SEND_POI(-4835.27, -1294.69, 6, 6, 0, "Deeprun Tram"); + player->SEND_GOSSIP_MENU(3814, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Gryphon Master + player->SEND_POI(-4821.52, -1152.3, 6, 6, 0, "Ironforge Gryphon Master"); + player->SEND_GOSSIP_MENU(2762, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Guild Master + player->SEND_POI(-5021, -996.45, 6, 6, 0, "Ironforge Visitor's Center"); + player->SEND_GOSSIP_MENU(2764, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Inn + player->SEND_POI(-4850.47, -872.57, 6, 6, 0, "Stonefire Tavern"); + player->SEND_GOSSIP_MENU(2768, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Mailbox + player->SEND_POI(-4845.7, -880.55, 6, 6, 0, "Ironforge Mailbox"); + player->SEND_GOSSIP_MENU(2769, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Stable Master + player->SEND_POI(-5010.2, -1262, 6, 6, 0, "Ulbrek Firehand"); + player->SEND_GOSSIP_MENU(5986, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Weapons Trainer + player->SEND_POI(-5040, -1201.88, 6, 6, 0, "Bixi and Buliwyf"); + player->SEND_GOSSIP_MENU(4518, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Battlemaster + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALTERACVALLEY , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ARATHIBASIN , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARSONGULCH , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(7529, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Class Trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PALADIN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARLOCK , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SHAMAN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->SEND_GOSSIP_MENU(2766, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Profession Trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(2793, _Creature->GetGUID()); + break; + } +} + +void SendBattleMasterMenu_guard_ironforge(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //AV + player->SEND_POI(-5047.87, -1263.77, 6, 6, 0, "Glordrum Steelbeard"); + player->SEND_GOSSIP_MENU(7483, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //AB + player->SEND_POI(-5038.37, -1266.39, 6, 6, 0, "Donal Osgood"); + player->SEND_GOSSIP_MENU(7649, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //WSG + player->SEND_POI(-5037.24, -1274.82, 6, 6, 0, "Lylandris"); + player->SEND_GOSSIP_MENU(7528, _Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_ironforge(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Hunter + player->SEND_POI(-5023, -1253.68, 6, 6, 0, "Hall of Arms"); + player->SEND_GOSSIP_MENU(2770, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Mage + player->SEND_POI(-4627, -926.45, 6, 6, 0, "Hall of Mysteries"); + player->SEND_GOSSIP_MENU(2771, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Paladin + player->SEND_POI(-4627.02, -926.45, 6, 6, 0, "Hall of Mysteries"); + player->SEND_GOSSIP_MENU(2773, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Priest + player->SEND_POI(-4627, -926.45, 6, 6, 0, "Hall of Mysteries"); + player->SEND_GOSSIP_MENU(2772, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Rogue + player->SEND_POI(-4647.83, -1124, 6, 6, 0, "Ironforge Rogue Trainer"); + player->SEND_GOSSIP_MENU(2774, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Warlock + player->SEND_POI(-4605, -1110.45, 6, 6, 0, "Ironforge Warlock Trainer"); + player->SEND_GOSSIP_MENU(2775, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Warrior + player->SEND_POI(-5023.08, -1253.68, 6, 6, 0, "Hall of Arms"); + player->SEND_GOSSIP_MENU(2776, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Shaman + player->SEND_POI(-4732, -1147, 6, 6, 0, "Ironforge Shaman Trainer"); + //incorrect id + player->SEND_GOSSIP_MENU(2766, _Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_ironforge(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(-4858.5, -1241.83, 6, 6, 0, "Berryfizz's Potions and Mixed Drinks"); + player->SEND_GOSSIP_MENU(2794, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(-4796.97, -1110.17, 6, 6, 0, "The Great Forge"); + player->SEND_GOSSIP_MENU(2795, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-4767.83, -1184.59, 6, 6, 0, "The Bronze Kettle"); + player->SEND_GOSSIP_MENU(2796, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(-4803.72, -1196.53, 6, 6, 0, "Thistlefuzz Arcanery"); + player->SEND_GOSSIP_MENU(2797, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_POI(-4799.56, -1250.23, 6, 6, 0, "Springspindle's Gadgets"); + player->SEND_GOSSIP_MENU(2798, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(-4881.6, -1153.13, 6, 6, 0, "Ironforge Physician"); + player->SEND_GOSSIP_MENU(2799, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(-4597.91, -1091.93, 6, 6, 0, "Traveling Fisherman"); + player->SEND_GOSSIP_MENU(2800, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Herbalism + player->SEND_POI(-4876.9, -1151.92, 6, 6, 0, "Ironforge Physician"); + player->SEND_GOSSIP_MENU(2801, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Leatherworking + player->SEND_POI(-4745, -1027.57, 6, 6, 0, "Finespindle's Leather Goods"); + player->SEND_GOSSIP_MENU(2802, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Minning + player->SEND_POI(-4705.06, -1116.43, 6, 6, 0, "Deepmountain Mining Guild"); + player->SEND_GOSSIP_MENU(2804, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Skinning + player->SEND_POI(-4745, -1027.57, 6, 6, 0, "Finespindle's Leather Goods"); + player->SEND_GOSSIP_MENU(2805, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Tailoring + player->SEND_POI(-4719.60, -1056.96, 6, 6, 0, "Stonebrow's Clothier"); + player->SEND_GOSSIP_MENU(2807, _Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_ironforge(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_ironforge(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_ironforge(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_ironforge(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_ironforge(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_ironforge end + *******************************************************/ + +CreatureAI* GetAI_guard_ironforge(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_mulgore start + *******************************************************/ + +bool GossipHello_guard_mulgore(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WINDRIDER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(3543,_Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_mulgore(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bank + player->SEND_GOSSIP_MENU(4051,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Wind rider + player->SEND_GOSSIP_MENU(4052,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Inn + player->SEND_POI(-2361.38, -349.19, 6, 6, 0, "Bloodhoof Village Inn"); + player->SEND_GOSSIP_MENU(4053,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Stable master + player->SEND_POI(-2338.86, -357.56, 6, 6, 0, "Seikwa"); + player->SEND_GOSSIP_MENU(5976,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DRUID , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SHAMAN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->SEND_GOSSIP_MENU(4069,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(4070,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_mulgore(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Druid + player->SEND_POI(-2312.15, -443.69, 6, 6, 0, "Gennia Runetotem"); + player->SEND_GOSSIP_MENU(4054,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Hunter + player->SEND_POI(-2178.14, -406.14, 6, 6, 0, "Yaw Sharpmane"); + player->SEND_GOSSIP_MENU(4055,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Shaman + player->SEND_POI(-2301.5, -439.87, 6, 6, 0, "Narm Skychaser"); + player->SEND_GOSSIP_MENU(4056,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Warrior + player->SEND_POI(-2345.43, -494.11, 6, 6, 0, "Krang Stonehoof"); + player->SEND_GOSSIP_MENU(4057,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_mulgore(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_GOSSIP_MENU(4058,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_GOSSIP_MENU(4059,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-2263.34, -287.91, 6, 6, 0, "Pyall Silentstride"); + player->SEND_GOSSIP_MENU(4060,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_GOSSIP_MENU(4061,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //First Aid + player->SEND_POI(-2353.52, -355.82, 6, 6, 0, "Vira Younghoof"); + player->SEND_GOSSIP_MENU(4062,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Fishing + player->SEND_POI(-2349.21, -241.37, 6, 6, 0, "Uthan Stillwater"); + player->SEND_GOSSIP_MENU(4063,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Herbalism + player->SEND_GOSSIP_MENU(4064,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Leatherworking + player->SEND_POI(-2257.12, -288.63, 6, 6, 0, "Chaw Stronghide"); + player->SEND_GOSSIP_MENU(4065,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Mining + player->SEND_GOSSIP_MENU(4066,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Skinning + player->SEND_POI(-2252.94, -291.32, 6, 6, 0, "Yonn Deepcut"); + player->SEND_GOSSIP_MENU(4067,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Tailoring + player->SEND_GOSSIP_MENU(4068,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_mulgore(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_mulgore(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_mulgore(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_mulgore(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_mulgore end + *******************************************************/ + +CreatureAI* GetAI_guard_mulgore(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_orgrimmar start + *******************************************************/ + +bool GossipHello_guard_orgrimmar(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WINDRIDER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ZEPPLINMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WEAPONMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_OFFICERS , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13); + player->SEND_GOSSIP_MENU(2593,_Creature->GetGUID()); + + return true; +} + +void SendDefaultMenu_guard_orgrimmar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bank + player->SEND_POI(1631.51, -4375.33, 6, 6, 0, "Bank of Orgrimmar"); + player->SEND_GOSSIP_MENU(2554,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //wind rider + player->SEND_POI(1676.6, -4332.72, 6, 6, 0, "The Sky Tower"); + player->SEND_GOSSIP_MENU(2555,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //guild master + player->SEND_POI(1576.93, -4294.75, 6, 6, 0, "Horde Embassy"); + player->SEND_GOSSIP_MENU(2556,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Inn + player->SEND_POI(1644.51, -4447.27, 6, 6, 0, "Orgrimmar Inn"); + player->SEND_GOSSIP_MENU(2557,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //mailbox + player->SEND_POI(1622.53, -4388.79, 6, 6, 0, "Orgrimmar Mailbox"); + player->SEND_GOSSIP_MENU(2558,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //auction house + player->SEND_POI(1679.21, -4450.1, 6, 6, 0, "Orgrimmar Auction House"); + player->SEND_GOSSIP_MENU(3075,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //zeppelin + player->SEND_POI(1337.36, -4632.7, 6, 6, 0, "Orgrimmar Zeppelin Tower"); + player->SEND_GOSSIP_MENU(3173,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //weapon master + player->SEND_POI(2092.56, -4823.95, 6, 6, 0, "Sayoc & Hanashi"); + player->SEND_GOSSIP_MENU(4519,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //stable master + player->SEND_POI(2133.12, -4663.93, 6, 6, 0, "Xon'cha"); + player->SEND_GOSSIP_MENU(5974,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //officers lounge + player->SEND_POI(1633.56, -4249.37, 6, 6, 0, "Hall of Legends"); + player->SEND_GOSSIP_MENU(7046,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //battlemaster + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALTERACVALLEY , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ARATHIBASIN , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARSONGULCH , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(7521,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SHAMAN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARLOCK , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PALADIN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->SEND_GOSSIP_MENU(2599,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 13: //profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(2594,_Creature->GetGUID()); + break; + } +} + +void SendBattleMasterMenu_guard_orgrimmar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //AV + player->SEND_POI(1983.92, -4794.2, 6, 6, 0, "Hall of the Brave"); + player->SEND_GOSSIP_MENU(7484,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //AB + player->SEND_POI(1983.92, -4794.2, 6, 6, 0, "Hall of the Brave"); + player->SEND_GOSSIP_MENU(7644,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //WSG + player->SEND_POI(1983.92, -4794.2, 6, 6, 0, "Hall of the Brave"); + player->SEND_GOSSIP_MENU(7520,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_orgrimmar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Hunter + player->SEND_POI(2114.84, -4625.31, 6, 6, 0, "Orgrimmar Hunter's Hall"); + player->SEND_GOSSIP_MENU(2559,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Mage + player->SEND_POI(1451.26, -4223.33, 6, 6, 0, "Darkbriar Lodge"); + player->SEND_GOSSIP_MENU(2560,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Priest + player->SEND_POI(1442.21, -4183.24, 6, 6, 0, "Spirit Lodge"); + player->SEND_GOSSIP_MENU(2561,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Shaman + player->SEND_POI(1925.34, -4181.89, 6, 6, 0, "Thrall's Fortress"); + player->SEND_GOSSIP_MENU(2562,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Rogue + player->SEND_POI(1773.39, -4278.97, 6, 6, 0, "Shadowswift Brotherhood"); + player->SEND_GOSSIP_MENU(2563,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Warlock + player->SEND_POI(1849.57, -4359.68, 6, 6, 0, "Darkfire Enclave"); + player->SEND_GOSSIP_MENU(2564,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Warrior + player->SEND_POI(1983.92, -4794.2, 6, 6, 0, "Hall of the Brave"); + player->SEND_GOSSIP_MENU(2565,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Paladin + player->SEND_POI(1906.65, -4134.26, 6, 6, 0, "Valley of Wisdom"); + player->SEND_GOSSIP_MENU(10843,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_orgrimmar(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(1955.17, -4475.79, 6, 6, 0, "Yelmak's Alchemy and Potions"); + player->SEND_GOSSIP_MENU(2497,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(2054.34, -4831.85, 6, 6, 0, "The Burning Anvil"); + player->SEND_GOSSIP_MENU(2499,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(1780.96, -4481.31, 6, 6, 0, "Borstan's Firepit"); + player->SEND_GOSSIP_MENU(2500,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(1917.5, -4434.95, 6, 6, 0, "Godan's Runeworks"); + player->SEND_GOSSIP_MENU(2501,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_POI(2038.45, -4744.75, 6, 6, 0, "Nogg's Machine Shop"); + player->SEND_GOSSIP_MENU(2653,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(1485.21, -4160.91, 6, 6, 0, "Survival of the Fittest"); + player->SEND_GOSSIP_MENU(2502,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(1994.15, -4655.7, 6, 6, 0, "Lumak's Fishing"); + player->SEND_GOSSIP_MENU(2503,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Herbalism + player->SEND_POI(1898.61, -4454.93, 6, 6, 0, "Jandi's Arboretum"); + player->SEND_GOSSIP_MENU(2504,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Leatherworking + player->SEND_POI(1852.82, -4562.31, 6, 6, 0, "Kodohide Leatherworkers"); + player->SEND_GOSSIP_MENU(2513,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Mining + player->SEND_POI(2029.79, -4704, 6, 6, 0, "Red Canyon Mining"); + player->SEND_GOSSIP_MENU(2515,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Skinning + player->SEND_POI(1852.82, -4562.31, 6, 6, 0, "Kodohide Leatherworkers"); + player->SEND_GOSSIP_MENU(2516,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Tailoring + player->SEND_POI(1802.66, -4560.66, 6, 6, 0, "Magar's Cloth Goods"); + player->SEND_GOSSIP_MENU(2518,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_orgrimmar(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_orgrimmar(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_orgrimmar(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_orgrimmar(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_orgrimmar(player, _Creature, action); break; + } + return true; +} + +bool ReceiveEmote_guard_orgrimmar(Player *player, Creature *_Creature, uint32 emote) +{ + if( player->GetTeam() == HORDE ) + DoReplyToTextEmote(_Creature,emote); + return true; +} + +/******************************************************* + * guard_orgrimmar end + *******************************************************/ + +CreatureAI* GetAI_guard_orgrimmar(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_shattrath start + *******************************************************/ + +bool GossipHello_guard_shattrath(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAVERN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FLIGHTMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MANALOOM , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMYLAB , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GEMMERCHANT , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(10321, _Creature->GetGUID()); + + return true; +} + +void SendDefaultMenu_guard_shattrath(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Tavern + player->SEND_POI(-1759.5, 5165, 6, 6, 0, "Worlds End Tavern"); + player->SEND_GOSSIP_MENU(10394, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Bank + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANKALDOR , GOSSIP_SENDER_SEC_BANK, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANKSCYERS , GOSSIP_SENDER_SEC_BANK, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(10379, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Inn + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INNALDOR , GOSSIP_SENDER_SEC_INN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INNSCYERS , GOSSIP_SENDER_SEC_INN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(10382, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Flight master + player->SEND_POI(-1832, 5299, 6, 6, 0, "Flight Master"); + player->SEND_GOSSIP_MENU(10385, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Mailbox + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANKALDOR , GOSSIP_SENDER_SEC_MAILBOX, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INNALDOR , GOSSIP_SENDER_SEC_MAILBOX, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANKSCYERS , GOSSIP_SENDER_SEC_MAILBOX, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INNSCYERS , GOSSIP_SENDER_SEC_MAILBOX, GOSSIP_ACTION_INFO_DEF + 4); + player->SEND_GOSSIP_MENU(10386, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Stable master + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEALDOR , GOSSIP_SENDER_SEC_STABLEMASTER, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLESCYERS , GOSSIP_SENDER_SEC_STABLEMASTER, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(10387, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Battlemaster + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTERALLIANCE , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTERHORDE , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTERARENA , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(10388, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Profession master + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_JEWELCRAFTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->SEND_GOSSIP_MENU(10391, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Mana Loom + player->SEND_POI(-2070, 5265.5, 6, 6, 0, "Mana Loom"); + player->SEND_GOSSIP_MENU(10503, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Alchemy Lab + player->SEND_POI(-1648.5, 5540, 6, 6, 0, "Alchemy Lab"); + player->SEND_GOSSIP_MENU(10321, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Gem Merchant + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GEMALDOR , GOSSIP_SENDER_SEC_GEMMERCHANT, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GEMSCYERS , GOSSIP_SENDER_SEC_GEMMERCHANT, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(10697, _Creature->GetGUID()); + break; + } +} + +void SendBankMenu_guard_shattrath(Player *player, Creature *_Creature, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->SEND_POI(-1730.5, 5496, 6, 6, 0, "Aldor Bank"); + player->SEND_GOSSIP_MENU(10380, _Creature->GetGUID()); + } + if (action == GOSSIP_ACTION_INFO_DEF + 2) + { + player->SEND_POI(-1997.7, 5363, 6, 6, 0, "Scyers Bank"); + player->SEND_GOSSIP_MENU(10381, _Creature->GetGUID()); + } +} + +void SendInnMenu_guard_shattrath(Player *player, Creature *_Creature, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->SEND_POI(-1895, 5767, 6, 6, 0, "Aldor Inn"); + player->SEND_GOSSIP_MENU(10383, _Creature->GetGUID()); + } + if (action == GOSSIP_ACTION_INFO_DEF + 2) + { + player->SEND_POI(-2178, 5405, 6, 6, 0, "Scyers Inn"); + player->SEND_GOSSIP_MENU(10384, _Creature->GetGUID()); + } +} + +void SendMailboxMenu_guard_shattrath(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: + player->SEND_POI(-1730.5, 5496, 6, 6, 0, "Aldor Bank"); + player->SEND_GOSSIP_MENU(10380, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + player->SEND_POI(-1895, 5767, 6, 6, 0, "Aldor Inn"); + player->SEND_GOSSIP_MENU(10383, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + player->SEND_POI(-1997.7, 5363, 6, 6, 0, "Scyers Bank"); + player->SEND_GOSSIP_MENU(10381, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: + player->SEND_POI(-2178, 5405, 6, 6, 0, "Scyers Inn"); + player->SEND_GOSSIP_MENU(10384, _Creature->GetGUID()); + break; + } +} + +void SendStableMasterMenu_guard_shattrath(Player *player, Creature *_Creature, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->SEND_POI(-1888.5, 5761, 6, 6, 0, "Aldor Stable"); + player->SEND_GOSSIP_MENU(10321, _Creature->GetGUID()); + } + if (action == GOSSIP_ACTION_INFO_DEF + 2) + { + player->SEND_POI(-2170, 5404, 6, 6, 0, "Scyers Stable"); + player->SEND_GOSSIP_MENU(10321, _Creature->GetGUID()); + } +} + +void SendBattleMasterMenu_guard_shattrath(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: + player->SEND_POI(-1774, 5251, 6, 6, 0, "Alliance Battlemasters"); + player->SEND_GOSSIP_MENU(10389, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + player->SEND_POI(-1963, 5263, 6, 6, 0, "Horde Battlemasters"); + player->SEND_GOSSIP_MENU(10390, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + player->SEND_POI(-1960, 5175, 6, 6, 0, "Arena Battlemasters"); + player->SEND_GOSSIP_MENU(12510, _Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_shattrath(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(-1648.5, 5534, 6, 6, 0, "Lorokeem"); + player->SEND_GOSSIP_MENU(10392, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(-1847, 5222, 6, 6, 0, "Kradu Grimblade and Zula Slagfury"); + player->SEND_GOSSIP_MENU(10400, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-2067.4, 5316.5, 6, 6, 0, "Jack Trapper"); + player->SEND_GOSSIP_MENU(10393, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(-2263.5, 5563.5, 6, 6, 0, "High Enchanter Bardolan"); + player->SEND_GOSSIP_MENU(10395, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //First Aid + player->SEND_POI(-1591, 5265.5, 6, 6, 0, "Mildred Fletcher"); + player->SEND_GOSSIP_MENU(10396, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Jewelcrafting + player->SEND_POI(-1654, 5667.5, 6, 6, 0, "Hamanar"); + player->SEND_GOSSIP_MENU(10397, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Leatherworking + player->SEND_POI(-2060.5, 5256.5, 6, 6, 0, "Darmari"); + player->SEND_GOSSIP_MENU(10399, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Skinning + player->SEND_POI(-2048, 5300, 6, 6, 0, "Seymour"); + player->SEND_GOSSIP_MENU(10398, _Creature->GetGUID()); + break; + } +} + +void SendGemMerchantMenu_guard_shattrath(Player *player, Creature *_Creature, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->SEND_POI(-1645, 5669.5, 6, 6, 0, "Aldor Gem Merchant"); + player->SEND_GOSSIP_MENU(10698, _Creature->GetGUID()); + } + if (action == GOSSIP_ACTION_INFO_DEF + 2) + { + player->SEND_POI(-2193, 5424.5, 6, 6, 0, "Scyers Gem Merchant"); + player->SEND_GOSSIP_MENU(10699, _Creature->GetGUID()); + } +} + +bool GossipSelect_guard_shattrath(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_shattrath(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BANK: SendBankMenu_guard_shattrath(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_INN: SendInnMenu_guard_shattrath(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_STABLEMASTER: SendStableMasterMenu_guard_shattrath(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_GEMMERCHANT: SendGemMerchantMenu_guard_shattrath(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_MAILBOX: SendMailboxMenu_guard_shattrath(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_shattrath(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_shattrath(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_shattrath end + *******************************************************/ + +CreatureAI* GetAI_guard_shattrath(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_shattrath_aldor + *******************************************************/ + +#define SPELL_BANISHED_SHATTRATH_A 36642 +#define SPELL_BANISHED_SHATTRATH_S 36671 +#define SPELL_BANISH_TELEPORT 36643 +#define SPELL_EXILE 39533 + +struct MANGOS_DLL_DECL guard_shattrath_aldorAI : public guardAI +{ + guard_shattrath_aldorAI(Creature *c) : guardAI(c) { Reset(); } + + uint32 Exile_Timer; + uint32 Banish_Timer; + uint64 playerGUID; + bool CanTeleport; + + void Reset() + { + Banish_Timer = 5000; + Exile_Timer = 8500; + playerGUID = 0; + CanTeleport = false; + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( CanTeleport ) + { + if( Exile_Timer < diff ) + { + if( Unit* temp = Unit::GetUnit(*m_creature,playerGUID) ) + { + temp->CastSpell(temp,SPELL_EXILE,true); + temp->CastSpell(temp,SPELL_BANISH_TELEPORT,true); + } + playerGUID = 0; + Exile_Timer = 8500; + CanTeleport = false; + }else Exile_Timer -= diff; + } + else if( Banish_Timer < diff ) + { + Unit* temp = m_creature->getVictim(); + if( temp && temp->GetTypeId() == TYPEID_PLAYER ) + { + DoCast(temp,SPELL_BANISHED_SHATTRATH_A); + Banish_Timer = 9000; + playerGUID = temp->GetGUID(); + if( playerGUID ) + CanTeleport = true; + } + }else Banish_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +bool GossipHello_guard_shattrath_aldor(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAVERN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FLIGHTMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MANALOOM , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMYLAB , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GEMMERCHANT , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(10524, _Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_shattrath_aldor(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Tavern + player->SEND_POI(-1759.5, 5165, 6, 6, 0, "Worlds End Tavern"); + player->SEND_GOSSIP_MENU(10394, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Bank + player->SEND_POI(-1730.5, 5496, 6, 6, 0, "Aldor Bank"); + player->SEND_GOSSIP_MENU(10380, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Inn + player->SEND_POI(-1895, 5767, 6, 6, 0, "Aldor Inn"); + player->SEND_GOSSIP_MENU(10525, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Flight master + player->SEND_POI(-1832, 5299, 6, 6, 0, "Shattrath Flight Master"); + player->SEND_GOSSIP_MENU(10402, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Mailbox + player->SEND_POI(0, 0, 6, 6, 0, "Aldor Mailbox"); + //unknown + player->SEND_GOSSIP_MENU(10524, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Stable master + player->SEND_POI(-1888.5, 5761, 6, 6, 0, "Aldor Stable Master"); + player->SEND_GOSSIP_MENU(10527, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Battlemaster + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTERALLIANCE , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTERHORDE , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTERARENA , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(10388, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Profession master + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_JEWELCRAFTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->SEND_GOSSIP_MENU(10391, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Mana Loom + player->SEND_POI(-2070, 5265.5, 6, 6, 0, "Mana Loom"); + player->SEND_GOSSIP_MENU(10522, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Alchemy Lab + player->SEND_POI(-1648.5, 5540, 6, 6, 0, "Alchemy Lab"); + player->SEND_GOSSIP_MENU(10696, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Gem Merchant + player->SEND_POI(-1645, 5669.5, 6, 6, 0, "Aldor Gem Merchant"); + player->SEND_GOSSIP_MENU(10411, _Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_shattrath_aldor(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(-1648.5, 5534, 6, 6, 0, "Lorokeem"); + player->SEND_GOSSIP_MENU(10392, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(-1847, 5222, 6, 6, 0, "Kradu Grimblade and Zula Slagfury"); + player->SEND_GOSSIP_MENU(10400, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-2067.4, 5316.5, 6, 6, 0, "Jack Trapper"); + player->SEND_GOSSIP_MENU(10393, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(-2263.5, 5563.5, 6, 6, 0, "High Enchanter Bardolan"); + player->SEND_GOSSIP_MENU(10528, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //First Aid + player->SEND_POI(-1591, 5265.5, 6, 6, 0, "Mildred Fletcher"); + player->SEND_GOSSIP_MENU(10396, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Jewelcrafting + player->SEND_POI(-1654, 5667.5, 6, 6, 0, "Hamanar"); + player->SEND_GOSSIP_MENU(10529, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Leatherworking + player->SEND_POI(-2060.5, 5256.5, 6, 6, 0, "Darmari"); + player->SEND_GOSSIP_MENU(10399, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Skinning + player->SEND_POI(-2048, 5300, 6, 6, 0, "Seymour"); + player->SEND_GOSSIP_MENU(10419, _Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_shattrath_aldor(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_shattrath_aldor(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_shattrath_aldor(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_shattrath(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_shattrath_aldor end + *******************************************************/ + +CreatureAI* GetAI_guard_shattrath_aldor(Creature *_Creature) +{ + return new guard_shattrath_aldorAI (_Creature); +} + +/******************************************************* + * guard_shattrath_scryer + *******************************************************/ + +struct MANGOS_DLL_DECL guard_shattrath_scryerAI : public guardAI +{ + guard_shattrath_scryerAI(Creature *c) : guardAI(c) { Reset(); } + + uint32 Exile_Timer; + uint32 Banish_Timer; + uint64 playerGUID; + bool CanTeleport; + + void Reset() + { + Banish_Timer = 5000; + Exile_Timer = 8500; + playerGUID = 0; + CanTeleport = false; + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( CanTeleport ) + { + if( Exile_Timer < diff ) + { + if( Unit* temp = Unit::GetUnit(*m_creature,playerGUID) ) + { + temp->CastSpell(temp,SPELL_EXILE,true); + temp->CastSpell(temp,SPELL_BANISH_TELEPORT,true); + } + playerGUID = 0; + Exile_Timer = 8500; + CanTeleport = false; + }else Exile_Timer -= diff; + } + else if( Banish_Timer < diff ) + { + Unit* temp = m_creature->getVictim(); + if( temp && temp->GetTypeId() == TYPEID_PLAYER ) + { + DoCast(temp,SPELL_BANISHED_SHATTRATH_S); + Banish_Timer = 9000; + playerGUID = temp->GetGUID(); + if( playerGUID ) + CanTeleport = true; + } + }else Banish_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +bool GossipHello_guard_shattrath_scryer(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAVERN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FLIGHTMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MANALOOM , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMYLAB , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GEMMERCHANT , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(10430, _Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_shattrath_scryer(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Tavern + player->SEND_POI(-1759.5, 5165, 6, 6, 0, "Worlds End Tavern"); + player->SEND_GOSSIP_MENU(10431, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Bank + player->SEND_POI(-1996.6, 5363.7, 6, 6, 0, "Scryer Bank"); + player->SEND_GOSSIP_MENU(10432, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Inn + player->SEND_POI(-2176.6, 5405.8, 6, 6, 0, "Scryer Inn"); + player->SEND_GOSSIP_MENU(10433, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Flight master + player->SEND_POI(-1832, 5299, 6, 6, 0, "Shattrath Flight Master"); + player->SEND_GOSSIP_MENU(10435, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Mailbox + player->SEND_POI(-2174.3, 5411.4, 6, 6, 0, "Scryer Mailbox"); + player->SEND_GOSSIP_MENU(10436, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Stable master + player->SEND_POI(-2169.9, 5405.1, 6, 6, 0, "Scryer Stable Master"); + player->SEND_GOSSIP_MENU(10437, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Battlemaster + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTERALLIANCE , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTERHORDE , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTERARENA , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(10438, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Profession master + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_JEWELCRAFTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->SEND_GOSSIP_MENU(10504, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Mana Loom + player->SEND_POI(-2070, 5265.5, 6, 6, 0, "Mana Loom"); + player->SEND_GOSSIP_MENU(10522, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Alchemy Lab + player->SEND_POI(-1648.5, 5540, 6, 6, 0, "Alchemy Lab"); + player->SEND_GOSSIP_MENU(10701, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Gem Merchant + player->SEND_POI(-1645, 5669.5, 6, 6, 0, "Scryer Gem Merchant"); + player->SEND_GOSSIP_MENU(10702, _Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_shattrath_scryer(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(-1648.5, 5534, 6, 6, 0, "Lorokeem"); + player->SEND_GOSSIP_MENU(10516, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(-1847, 5222, 6, 6, 0, "Kradu Grimblade and Zula Slagfury"); + player->SEND_GOSSIP_MENU(10517, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-2067.4, 5316.5, 6, 6, 0, "Jack Trapper"); + player->SEND_GOSSIP_MENU(10518, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(-2263.5, 5563.5, 6, 6, 0, "High Enchanter Bardolan"); + player->SEND_GOSSIP_MENU(10519, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //First Aid + player->SEND_POI(-1591, 5265.5, 6, 6, 0, "Mildred Fletcher"); + player->SEND_GOSSIP_MENU(10520, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Jewelcrafting + player->SEND_POI(-1654, 5667.5, 6, 6, 0, "Hamanar"); + player->SEND_GOSSIP_MENU(10521, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Leatherworking + player->SEND_POI(-2060.5, 5256.5, 6, 6, 0, "Darmari"); + player->SEND_GOSSIP_MENU(10523, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Skinning + player->SEND_POI(-2048, 5300, 6, 6, 0, "Seymour"); + player->SEND_GOSSIP_MENU(10523, _Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_shattrath_scryer(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_shattrath_scryer(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_shattrath_scryer(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_shattrath(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_shattrath_scryer end + *******************************************************/ + +CreatureAI* GetAI_guard_shattrath_scryer(Creature *_Creature) +{ + return new guard_shattrath_scryerAI (_Creature); +} + +/******************************************************* + * guard_silvermoon start + *******************************************************/ + +bool GossipHello_guard_silvermoon(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WEAPONMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WINDRIDER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(9316, _Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_silvermoon(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Auction house + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_AH_SILVERMOON_1 , GOSSIP_SENDER_SEC_AUCTIONHOUSE, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_AH_SILVERMOON_2 , GOSSIP_SENDER_SEC_AUCTIONHOUSE, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(9317, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Bank + player->SEND_POI(9808.4, -7488.16, 6, 6, 0, "Silvermoon Bank"); + player->SEND_GOSSIP_MENU(9322, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Guild master + player->SEND_POI(9474.97, -7345.21, 6, 6, 0, "Tandrine"); + player->SEND_GOSSIP_MENU(9324, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Inn + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN_SILVERMOON_1 , GOSSIP_SENDER_SEC_INN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN_SILVERMOON_2 , GOSSIP_SENDER_SEC_INN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(9602, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Mailbox + player->SEND_POI(9658.33, -7492.17, 6, 6, 0, "Silvermoon Mailbox"); + player->SEND_GOSSIP_MENU(9326, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Stable master + player->SEND_POI(9904.95, -7404.31, 6, 6, 0, "Shalenn"); + player->SEND_GOSSIP_MENU(9327, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Weapon trainer + player->SEND_POI(9841.17, -7505.13, 6, 6, 0, "Ileda"); + player->SEND_GOSSIP_MENU(9328, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Wind master + player->SEND_POI(9378.45, -7163.94, 6, 6, 0, "Silvermoon Wind Master"); + player->SEND_GOSSIP_MENU(10181, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Battlemaster + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALTERACVALLEY , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ARATHIBASIN , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ARENA , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_EYEOFTHESTORM , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARSONGULCH , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(9329, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DRUID , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PALADIN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARLOCK , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(9331, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_JEWELCRAFTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 13); + player->SEND_GOSSIP_MENU(9338, _Creature->GetGUID()); + break; + } +} + +void SendAuctionhouseMenu_guard_silvermoon(Player *player, Creature *_Creature, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->SEND_POI(9644.47, -7140.22, 6, 6, 0, "Western Auction House"); + player->SEND_GOSSIP_MENU(9318, _Creature->GetGUID()); + } + if (action == GOSSIP_ACTION_INFO_DEF + 2) + { + player->SEND_POI(9683.27, -7521.22, 6, 6, 0, "Royal Exchange Auction House"); + player->SEND_GOSSIP_MENU(9319, _Creature->GetGUID()); + } +} + +void SendInnMenu_guard_silvermoon(Player *player, Creature *_Creature, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->SEND_POI(9677.7, -7368, 6, 6, 0, "Silvermoon City Inn"); + player->SEND_GOSSIP_MENU(9325, _Creature->GetGUID()); + } + if (action == GOSSIP_ACTION_INFO_DEF + 2) + { + player->SEND_POI(9561.1, -7517.5, 6, 6, 0, "Wayfarer's Rest tavern"); + player->SEND_GOSSIP_MENU(9603, _Creature->GetGUID()); + } +} + +void SendBattleMasterMenu_guard_silvermoon(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //AV + player->SEND_POI(9850.49, -7572.26, 6, 6, 0, "Gurak"); + player->SEND_GOSSIP_MENU(9329, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //AB + player->SEND_POI(9857.18, -7564.36, 6, 6, 0, "Karen Wentworth"); + player->SEND_GOSSIP_MENU(9329, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //A + player->SEND_POI(9850.6, -7559.25, 6, 6, 0, "Bipp Glizzitor"); + player->SEND_GOSSIP_MENU(9329, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //EOS + player->SEND_POI(9857.18, -7564.36, 6, 6, 0, "Karen Wentworth"); + player->SEND_GOSSIP_MENU(9329, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //WSG + player->SEND_POI(9845.45, -7562.58, 6, 6, 0, "Krukk"); + player->SEND_GOSSIP_MENU(9329, _Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_silvermoon(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Druid + player->SEND_POI(9700.55, -7262.57, 6, 6, 0, "Harene Plainwalker"); + player->SEND_GOSSIP_MENU(9330, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Hunter + player->SEND_POI(9927.48, -7426.14, 6, 6, 0, "Zandine"); + player->SEND_GOSSIP_MENU(9332, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Mage + player->SEND_POI(9995.07, -7118.17, 6, 6, 0, "Quithas"); + player->SEND_GOSSIP_MENU(9333, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Paladin + player->SEND_POI(9850.22, -7516.93, 6, 6, 0, "Champion Bachi"); + player->SEND_GOSSIP_MENU(9334, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Priest + player->SEND_POI(9926.79, -7066.66, 6, 6, 0, "Belestra"); + player->SEND_GOSSIP_MENU(9335, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Rogue + player->SEND_POI(9739.88, -7374.33, 6, 6, 0, "Zelanis"); + player->SEND_GOSSIP_MENU(9336, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Warlock + player->SEND_POI(9787.57, -7284.63, 6, 6, 0, "Alamma"); + player->SEND_GOSSIP_MENU(9337, _Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_silvermoon(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(9998.09, -7214.36, 6, 6, 0, "Silvermoon Alchemy Trainer"); + player->SEND_GOSSIP_MENU(9316, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(9841.43, -7361.53, 6, 6, 0, "Silvermoon Blacksmithing Trainer"); + player->SEND_GOSSIP_MENU(9340, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(9577.26, -7243.6, 6, 6, 0, "Silvermoon Cooking Trainer"); + player->SEND_GOSSIP_MENU(9316, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(9962.57, -7246.18, 6, 6, 0, "Silvermoon Enchanting Trainer"); + player->SEND_GOSSIP_MENU(9341, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_POI(9820.18, -7329.56, 6, 6, 0, "Silvermoon Engineering Trainer"); + player->SEND_GOSSIP_MENU(9316, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(9579.8, -7343.71, 6, 6, 0, "Silvermoon First Aid Trainer"); + player->SEND_GOSSIP_MENU(9316, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(9602.73, -7328.3, 6, 6, 0, "Silvermoon Fishing Trainer"); + player->SEND_GOSSIP_MENU(9316, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Jewelcrafting + player->SEND_POI(9553.54, -7506.43, 6, 6, 0, "Silvermoon Jewelcrafting Trainer"); + player->SEND_GOSSIP_MENU(9346, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Herbalism + player->SEND_POI(10004.4, -7216.86, 6, 6, 0, "Silvermoon Herbalism Trainer"); + player->SEND_GOSSIP_MENU(9316, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Leatherworking + player->SEND_POI(9503.72, -7430.16, 6, 6, 0, "Silvermoon Leatherworking Trainer"); + player->SEND_GOSSIP_MENU(9347, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Mining + player->SEND_POI(9805.1, -7355.56, 6, 6, 0, "Silvermoon Mining Trainer"); + player->SEND_GOSSIP_MENU(9348, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Skinning + player->SEND_POI(9513.37, -7429.4, 6, 6, 0, "Silvermoon Skinning Trainer"); + player->SEND_GOSSIP_MENU(9316, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 13: //Tailoring + player->SEND_POI(9750.55, -7095.28, 6, 6, 0, "Silvermoon Tailor"); + player->SEND_GOSSIP_MENU(9350, _Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_silvermoon(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_silvermoon(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_AUCTIONHOUSE: SendAuctionhouseMenu_guard_silvermoon(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_INN: SendInnMenu_guard_silvermoon(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_silvermoon(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_silvermoon(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_silvermoon(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_silvermoon end + *******************************************************/ + +CreatureAI* GetAI_guard_silvermoon(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_stormwind start + *******************************************************/ + +bool GossipHello_guard_stormwind(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STORMWIND_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DEEPRUNTRAM , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GRYPHON , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WEAPONMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_OFFICERS , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13); + player->SEND_GOSSIP_MENU(933,_Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_stormwind(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Auction House + player->SEND_POI(-8811.46, 667.46, 6, 6, 0, "Stormwind Auction House"); + player->SEND_GOSSIP_MENU(3834,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Bank + player->SEND_POI(-8916.87, 622.87, 6, 6, 0, "Stormwind Bank"); + player->SEND_GOSSIP_MENU(764,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Deeprun tram + player->SEND_POI(-8378.88, 554.23, 6, 6, 0, "The Deeprun Tram"); + player->SEND_GOSSIP_MENU(3813,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Inn + player->SEND_POI(-8869.0, 675.4, 6, 6, 0, "The Gilded Rose"); + player->SEND_GOSSIP_MENU(3860,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Gryphon Master + player->SEND_POI(-8837.0, 493.5, 6, 6, 0, "Stormwind Gryphon Master"); + player->SEND_GOSSIP_MENU(879,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Guild Master + player->SEND_POI(-8894.0, 611.2, 6, 6, 0, "Stormwind Vistor`s Center"); + player->SEND_GOSSIP_MENU(882,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Mailbox + player->SEND_POI(-8876.48, 649.18, 6, 6, 0, "Stormwind Mailbox"); + player->SEND_GOSSIP_MENU(3861,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Stable Master + player->SEND_POI(-8433.0, 554.7, 6, 6, 0, "Jenova Stoneshield"); + player->SEND_GOSSIP_MENU(5984,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Weapon Trainer + player->SEND_POI(-8797.0, 612.8, 6, 6, 0, "Woo Ping"); + player->SEND_GOSSIP_MENU(4516,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Officers Lounge + player->SEND_POI(-8759.92, 399.69, 6, 6, 0, "Champions` Hall"); + player->SEND_GOSSIP_MENU(7047,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Battlemasters + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALTERACVALLEY , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ARATHIBASIN , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARSONGULCH , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(7499,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Class trainers + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DRUID , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PALADIN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARLOCK , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SHAMAN , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->SEND_GOSSIP_MENU(898,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 13: //Profession trainers + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(918,_Creature->GetGUID()); + break; + } +} + +void SendBattleMasterMenu_guard_stormwind(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //AV + player->SEND_POI(-8443.88, 335.99, 6, 6, 0, "Thelman Slatefist"); + player->SEND_GOSSIP_MENU(7500, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //AB + player->SEND_POI(-8443.88, 335.99, 6, 6, 0, "Lady Hoteshem"); + player->SEND_GOSSIP_MENU(7650, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //WSG + player->SEND_POI(-8443.88, 335.99, 6, 6, 0, "Elfarran"); + player->SEND_GOSSIP_MENU(7501, _Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_stormwind(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Mage + player->SEND_POI(-9012.0, 867.6, 6, 6, 0, "Wizard`s Sanctum"); + player->SEND_GOSSIP_MENU(899,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Rogue + player->SEND_POI(-8753.0, 367.8, 6, 6, 0, "Stormwind - Rogue House"); + player->SEND_GOSSIP_MENU(900,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Warrior + player->SEND_POI(-8624.54, 402.61, 6, 6, 0, "Pig and Whistle Tavern"); + player->SEND_GOSSIP_MENU(901,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Druid + player->SEND_POI(-8751.0, 1124.5, 6, 6, 0, "The Park"); + player->SEND_GOSSIP_MENU(902,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Priest + player->SEND_POI(-8512.0, 862.4, 6, 6, 0, "Catedral Of Light"); + player->SEND_GOSSIP_MENU(903,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Paladin + player->SEND_POI(-8577.0, 881.7, 6, 6, 0, "Catedral Of Light"); + player->SEND_GOSSIP_MENU(904,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Hunter + player->SEND_POI(-8413.0, 541.5, 6, 6, 0, "Hunter Lodge"); + player->SEND_GOSSIP_MENU(905,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Warlock + player->SEND_POI(-8948.91, 998.35, 6, 6, 0, "The Slaughtered Lamb"); + player->SEND_GOSSIP_MENU(906,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Shaman + player->SEND_POI(-9033, 550, 6, 6, 0, "Valley Of Heroes"); + //incorrect id + player->SEND_GOSSIP_MENU(2593,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_stormwind(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(-8988.0, 759.60, 6, 6, 0, "Alchemy Needs"); + player->SEND_GOSSIP_MENU(919,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(-8424.0, 616.9, 6, 6, 0, "Therum Deepforge"); + player->SEND_GOSSIP_MENU(920,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(-8611.0, 364.6, 6, 6, 0, "Pig and Whistle Tavern"); + player->SEND_GOSSIP_MENU(921,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(-8858.0, 803.7, 6, 6, 0, "Lucan Cordell"); + player->SEND_GOSSIP_MENU(941,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_POI(-8347.0, 644.1, 6, 6, 0, "Lilliam Sparkspindle"); + player->SEND_GOSSIP_MENU(922,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(-8513.0, 801.8, 6, 6, 0, "Shaina Fuller"); + player->SEND_GOSSIP_MENU(923,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(-8803.0, 767.5, 6, 6, 0, "Arnold Leland"); + player->SEND_GOSSIP_MENU(940,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Herbalism + player->SEND_POI(-8967.0, 779.5, 6, 6, 0, "Alchemy Needs"); + player->SEND_GOSSIP_MENU(924,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Leatherworking + player->SEND_POI(-8726.0, 477.4, 6, 6, 0, "The Protective Hide"); + player->SEND_GOSSIP_MENU(925,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Mining + player->SEND_POI(-8434.0, 692.8, 6, 6, 0, "Gelman Stonehand"); + player->SEND_GOSSIP_MENU(927,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Skinning + player->SEND_POI(-8716.0, 469.4, 6, 6, 0, "The Protective Hide"); + player->SEND_GOSSIP_MENU(928,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Tailoring + player->SEND_POI(-8938.0, 800.7, 6, 6, 0, "Duncan`s Textiles"); + player->SEND_GOSSIP_MENU(929,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_stormwind(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_stormwind(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_stormwind(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_stormwind(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_stormwind(player, _Creature, action); break; + } + return true; +} + +bool ReceiveEmote_guard_stormwind(Player *player, Creature *_Creature, uint32 emote) +{ + if( player->GetTeam() == ALLIANCE ) + DoReplyToTextEmote(_Creature,emote); + return true; +} + +/******************************************************* + * guard_stormwind end + *******************************************************/ + +CreatureAI* GetAI_guard_stormwind(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_teldrassil start + *******************************************************/ + +bool GossipHello_guard_teldrassil(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FERRY , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(4316,_Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_teldrassil(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bank + player->SEND_GOSSIP_MENU(4317,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Rut`theran + player->SEND_GOSSIP_MENU(4318,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Guild master + player->SEND_GOSSIP_MENU(4319,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Inn + player->SEND_POI(9821.49, 960.13, 6, 6, 0, "Dolanaar Inn"); + player->SEND_GOSSIP_MENU(4320,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //stable master + player->SEND_POI(9808.37, 931.1, 6, 6, 0, "Seriadne"); + player->SEND_GOSSIP_MENU(5982,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_DRUID , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HUNTER , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(4264,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->SEND_GOSSIP_MENU(4273,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_teldrassil(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Druid + player->SEND_POI(9741.58, 963.7, 6, 6, 0, "Kal"); + player->SEND_GOSSIP_MENU(4323,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Hunter + player->SEND_POI(9815.12, 926.28, 6, 6, 0, "Dazalar"); + player->SEND_GOSSIP_MENU(4324,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Priest + player->SEND_POI(9906.16, 986.63, 6, 6, 0, "Laurna Morninglight"); + player->SEND_GOSSIP_MENU(4325,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Rogue + player->SEND_POI(9789, 942.86, 6, 6, 0, "Jannok Breezesong"); + player->SEND_GOSSIP_MENU(4326,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Warrior + player->SEND_POI(9821.96, 950.61, 6, 6, 0, "Kyra Windblade"); + player->SEND_GOSSIP_MENU(4327,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_teldrassil(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(9767.59, 878.81, 6, 6, 0, "Cyndra Kindwhisper"); + player->SEND_GOSSIP_MENU(4329,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Cooking + player->SEND_POI(9751.19, 906.13, 6, 6, 0, "Zarrin"); + player->SEND_GOSSIP_MENU(4330,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Enchanting + player->SEND_POI(10677.59, 1946.56, 6, 6, 0, "Alanna Raveneye"); + player->SEND_GOSSIP_MENU(4331,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //First Aid + player->SEND_POI(9903.12, 999, 6, 6, 0, "Byancie"); + player->SEND_GOSSIP_MENU(4332,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Fishing + player->SEND_GOSSIP_MENU(4333,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Herbalism + player->SEND_POI(9773.78, 875.88, 6, 6, 0, "Malorne Bladeleaf"); + player->SEND_GOSSIP_MENU(4334,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Leatherworking + player->SEND_POI(10152.59, 1681.46, 6, 6, 0, "Nadyia Maneweaver"); + player->SEND_GOSSIP_MENU(4335,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Skinning + player->SEND_POI(10135.59, 1673.18, 6, 6, 0, "Radnaal Maneweaver"); + player->SEND_GOSSIP_MENU(4336,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Tailoring + player->SEND_GOSSIP_MENU(4337,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_teldrassil(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_teldrassil(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_teldrassil(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_teldrassil(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_teldrassil end + *******************************************************/ + +CreatureAI* GetAI_guard_teldrassil(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_tirisfal start + *******************************************************/ + +bool GossipHello_guard_tirisfal(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATHANDLER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(4097,_Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_tirisfal(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bank + player->SEND_GOSSIP_MENU(4074,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //bat handler + player->SEND_GOSSIP_MENU(4075,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Inn + player->SEND_POI(2246.68, 241.89, 6, 6, 0, "Gallows` End Tavern"); + player->SEND_GOSSIP_MENU(4076,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Stable Master + player->SEND_POI(2267.66, 319.32, 6, 6, 0, "Morganus"); + player->SEND_GOSSIP_MENU(5978,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARLOCK , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(4292,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(4096,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_tirisfal(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Mage + player->SEND_POI(2259.18, 240.93, 6, 6, 0, "Cain Firesong"); + player->SEND_GOSSIP_MENU(4077,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Priest + player->SEND_POI(2259.18, 240.93, 6, 6, 0, "Dark Cleric Beryl"); + player->SEND_GOSSIP_MENU(4078,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Rogue + player->SEND_POI(2259.18, 240.93, 6, 6, 0, "Marion Call"); + player->SEND_GOSSIP_MENU(4079,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Warlock + player->SEND_POI(2259.18, 240.93, 6, 6, 0, "Rupert Boch"); + player->SEND_GOSSIP_MENU(4080,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Warrior + player->SEND_POI(2256.48, 240.32, 6, 6, 0, "Austil de Mon"); + player->SEND_GOSSIP_MENU(4081,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_tirisfal(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(2263.25, 344.23, 6, 6, 0, "Carolai Anise"); + player->SEND_GOSSIP_MENU(4082,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_GOSSIP_MENU(4083,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_GOSSIP_MENU(4084,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(2250.35, 249.12, 6, 6, 0, "Vance Undergloom"); + player->SEND_GOSSIP_MENU(4085,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_GOSSIP_MENU(4086,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(2246.68, 241.89, 6, 6, 0, "Nurse Neela"); + player->SEND_GOSSIP_MENU(4087,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(2292.37, -10.72, 6, 6, 0, "Clyde Kellen"); + player->SEND_GOSSIP_MENU(4088,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Herbalism + player->SEND_POI(2268.21, 331.69, 6, 6, 0, "Faruza"); + player->SEND_GOSSIP_MENU(4089,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Leatherworking + player->SEND_POI(2027, 78.72, 6, 6, 0, "Shelene Rhobart"); + player->SEND_GOSSIP_MENU(4090,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Mining + player->SEND_GOSSIP_MENU(4091,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Skinning + player->SEND_POI(2027, 78.72, 6, 6, 0, "Rand Rhobart"); + player->SEND_GOSSIP_MENU(4092,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Tailoring + player->SEND_POI(2160.45, 659.93, 6, 6, 0, "Bowen Brisboise"); + player->SEND_GOSSIP_MENU(4093,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_tirisfal(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_tirisfal(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_tirisfal(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_tirisfal(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_tirisfal end + *******************************************************/ + +CreatureAI* GetAI_guard_tirisfal(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * guard_undercity start + *******************************************************/ + +bool GossipHello_guard_undercity(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATHANDLER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_GUILDMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_INN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAILBOX , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ZEPPLINMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WEAPONMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_STABLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BATTLEMASTER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(3543,_Creature->GetGUID()); + return true; +} + +void SendDefaultMenu_guard_undercity(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Bank + player->SEND_POI(1595.64, 232.45, 6, 6, 0, "Undercity Bank"); + player->SEND_GOSSIP_MENU(3514,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Bat handler + player->SEND_POI(1565.9, 271.43, 6, 6, 0, "Undercity Bat Handler"); + player->SEND_GOSSIP_MENU(3515,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Guild master + player->SEND_POI(1594.17, 205.57, 6, 6, 0, "Undercity Guild Master"); + player->SEND_GOSSIP_MENU(3516,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Inn + player->SEND_POI(1639.43, 220.99, 6, 6, 0, "Undercity Inn"); + player->SEND_GOSSIP_MENU(3517,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Mailbox + player->SEND_POI(1632.68, 219.4, 6, 6, 0, "Undercity Mailbox"); + player->SEND_GOSSIP_MENU(3518,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //Auction House + player->SEND_POI(1647.9, 258.49, 6, 6, 0, "Undercity Auction House"); + player->SEND_GOSSIP_MENU(3519,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Zeppelin + player->SEND_POI(2059, 274.86, 6, 6, 0, "Undercity Zeppelin"); + player->SEND_GOSSIP_MENU(3520,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Weapon Master + player->SEND_POI(1670.31, 324.66, 6, 6, 0, "Archibald"); + player->SEND_GOSSIP_MENU(4521,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Stable master + player->SEND_POI(1634.18, 226.76, 6, 6, 0, "Anya Maulray"); + player->SEND_GOSSIP_MENU(5979,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Battlemaster + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALTERACVALLEY , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ARATHIBASIN , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARSONGULCH , GOSSIP_SENDER_SEC_BATTLEINFO, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(7527,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Class trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MAGE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_PRIEST , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ROGUE , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARLOCK , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_WARRIOR , GOSSIP_SENDER_SEC_CLASSTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(3542,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Profession trainer + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ALCHEMY , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_BLACKSMITHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_COOKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENCHANTING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_ENGINEERING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FIRSTAID , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_FISHING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_HERBALISM , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_LEATHERWORKING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_MINING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_SKINNING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM( 0, GOSSIP_TEXT_TAILORING , GOSSIP_SENDER_SEC_PROFTRAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(3541,_Creature->GetGUID()); + break; + } +} + +void SendBattleMasterMenu_guard_undercity(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //AV + player->SEND_POI(1329, 333.92, 6, 6, 0, "Grizzle Halfmane"); + player->SEND_GOSSIP_MENU(7525,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //AB + player->SEND_POI(1283.3, 287.16, 6, 6, 0, "Sir Malory Wheeler"); + player->SEND_GOSSIP_MENU(7646,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //WSG + player->SEND_POI(1265, 351.18, 6, 6, 0, "Kurden Bloodclaw"); + player->SEND_GOSSIP_MENU(7526,_Creature->GetGUID()); + break; + } +} + +void SendClassTrainerMenu_guard_undercity(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Mage + player->SEND_POI(1781, 53, 6, 6, 0, "Undercity Mage Trainers"); + player->SEND_GOSSIP_MENU(3513,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Priest + player->SEND_POI(1758.33, 401.5, 6, 6, 0, "Undercity Priest Trainers"); + player->SEND_GOSSIP_MENU(3521,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Rogue + player->SEND_POI(1418.56, 65, 6, 6, 0, "Undercity Rogue Trainers"); + player->SEND_GOSSIP_MENU(3524,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Warlock + player->SEND_POI(1780.92, 53.16, 6, 6, 0, "Undercity Warlock Trainers"); + player->SEND_GOSSIP_MENU(3526,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Warrior + player->SEND_POI(1775.59, 418.19, 6, 6, 0, "Undercity Warrior Trainers"); + player->SEND_GOSSIP_MENU(3527,_Creature->GetGUID()); + break; + } +} + +void SendProfTrainerMenu_guard_undercity(Player *player, Creature *_Creature, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: //Alchemy + player->SEND_POI(1419.82, 417.19, 6, 6, 0, "The Apothecarium"); + player->SEND_GOSSIP_MENU(3528,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: //Blacksmithing + player->SEND_POI(1696, 285, 6, 6, 0, "Undercity Blacksmithing Trainer"); + player->SEND_GOSSIP_MENU(3529,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: //Cooking + player->SEND_POI(1596.34, 274.68, 6, 6, 0, "Undercity Cooking Trainer"); + player->SEND_GOSSIP_MENU(3530,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: //Enchanting + player->SEND_POI(1488.54, 280.19, 6, 6, 0, "Undercity Enchanting Trainer"); + player->SEND_GOSSIP_MENU(3531,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: //Engineering + player->SEND_POI(1408.58, 143.43, 6, 6, 0, "Undercity Engineering Trainer"); + player->SEND_GOSSIP_MENU(3532,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: //First Aid + player->SEND_POI(1519.65, 167.19, 6, 6, 0, "Undercity First Aid Trainer"); + player->SEND_GOSSIP_MENU(3533,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: //Fishing + player->SEND_POI(1679.9, 89, 6, 6, 0, "Undercity Fishing Trainer"); + player->SEND_GOSSIP_MENU(3534,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 8: //Herbalism + player->SEND_POI(1558, 349.36, 6, 6, 0, "Undercity Herbalism Trainer"); + player->SEND_GOSSIP_MENU(3535,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 9: //Leatherworking + player->SEND_POI(1498.76, 196.43, 6, 6, 0, "Undercity Leatherworking Trainer"); + player->SEND_GOSSIP_MENU(3536,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: //Mining + player->SEND_POI(1642.88, 335.58, 6, 6, 0, "Undercity Mining Trainer"); + player->SEND_GOSSIP_MENU(3537,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: //Skinning + player->SEND_POI(1498.6, 196.46, 6, 6, 0, "Undercity Skinning Trainer"); + player->SEND_GOSSIP_MENU(3538,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: //Tailoring + player->SEND_POI(1689.55, 193, 6, 6, 0, "Undercity Tailoring Trainer"); + player->SEND_GOSSIP_MENU(3539,_Creature->GetGUID()); + break; + } +} + +bool GossipSelect_guard_undercity(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (sender) + { + case GOSSIP_SENDER_MAIN: SendDefaultMenu_guard_undercity(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_CLASSTRAIN: SendClassTrainerMenu_guard_undercity(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_PROFTRAIN: SendProfTrainerMenu_guard_undercity(player, _Creature, action); break; + case GOSSIP_SENDER_SEC_BATTLEINFO: SendBattleMasterMenu_guard_undercity(player, _Creature, action); break; + } + return true; +} + +/******************************************************* + * guard_undercity end + *******************************************************/ + +CreatureAI* GetAI_guard_undercity(Creature *_Creature) +{ + return new guardAI (_Creature); +} + +/******************************************************* + * AddSC + *******************************************************/ + +void AddSC_guards() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="guard_azuremyst"; + newscript->pGossipHello = &GossipHello_guard_azuremyst; + newscript->pGossipSelect = &GossipSelect_guard_azuremyst; + newscript->GetAI = GetAI_guard_azuremyst; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_bluffwatcher"; + newscript->pGossipHello = &GossipHello_guard_bluffwatcher; + newscript->pGossipSelect = &GossipSelect_guard_bluffwatcher; + newscript->GetAI = GetAI_guard_bluffwatcher; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_contested"; + newscript->GetAI = GetAI_guard_contested; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_darnassus"; + newscript->pGossipHello = &GossipHello_guard_darnassus; + newscript->pGossipSelect = &GossipSelect_guard_darnassus; + newscript->GetAI = GetAI_guard_darnassus; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_dunmorogh"; + newscript->pGossipHello = &GossipHello_guard_dunmorogh; + newscript->pGossipSelect = &GossipSelect_guard_dunmorogh; + newscript->GetAI = GetAI_guard_dunmorogh; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_durotar"; + newscript->pGossipHello = &GossipHello_guard_durotar; + newscript->pGossipSelect = &GossipSelect_guard_durotar; + newscript->GetAI = GetAI_guard_durotar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_elwynnforest"; + newscript->pGossipHello = &GossipHello_guard_elwynnforest; + newscript->pGossipSelect = &GossipSelect_guard_elwynnforest; + newscript->GetAI = GetAI_guard_elwynnforest; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_eversong"; + newscript->pGossipHello = &GossipHello_guard_eversong; + newscript->pGossipSelect = &GossipSelect_guard_eversong; + newscript->GetAI = GetAI_guard_eversong; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_exodar"; + newscript->pGossipHello = &GossipHello_guard_exodar; + newscript->pGossipSelect = &GossipSelect_guard_exodar; + newscript->GetAI = GetAI_guard_exodar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_ironforge"; + newscript->pGossipHello = &GossipHello_guard_ironforge; + newscript->pGossipSelect = &GossipSelect_guard_ironforge; + newscript->GetAI = GetAI_guard_ironforge; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_mulgore"; + newscript->pGossipHello = &GossipHello_guard_mulgore; + newscript->pGossipSelect = &GossipSelect_guard_mulgore; + newscript->GetAI = GetAI_guard_mulgore; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_orgrimmar"; + newscript->pGossipHello = &GossipHello_guard_orgrimmar; + newscript->pGossipSelect = &GossipSelect_guard_orgrimmar; + newscript->pReceiveEmote = &ReceiveEmote_guard_orgrimmar; + newscript->GetAI = GetAI_guard_orgrimmar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_shattrath"; + newscript->pGossipHello = &GossipHello_guard_shattrath; + newscript->pGossipSelect = &GossipSelect_guard_shattrath; + newscript->GetAI = GetAI_guard_shattrath; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_shattrath_aldor"; + newscript->GetAI = GetAI_guard_shattrath_aldor; + newscript->pGossipHello = &GossipHello_guard_shattrath_aldor; + newscript->pGossipSelect = &GossipSelect_guard_shattrath_aldor; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_shattrath_scryer"; + newscript->GetAI = GetAI_guard_shattrath_scryer; + newscript->pGossipHello = &GossipHello_guard_shattrath_scryer; + newscript->pGossipSelect = &GossipSelect_guard_shattrath_scryer; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_silvermoon"; + newscript->pGossipHello = &GossipHello_guard_silvermoon; + newscript->pGossipSelect = &GossipSelect_guard_silvermoon; + newscript->GetAI = GetAI_guard_silvermoon; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_stormwind"; + newscript->pGossipHello = &GossipHello_guard_stormwind; + newscript->pGossipSelect = &GossipSelect_guard_stormwind; + newscript->pReceiveEmote = &ReceiveEmote_guard_stormwind; + newscript->GetAI = GetAI_guard_stormwind; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_teldrassil"; + newscript->pGossipHello = &GossipHello_guard_teldrassil; + newscript->pGossipSelect = &GossipSelect_guard_teldrassil; + newscript->GetAI = GetAI_guard_teldrassil; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_tirisfal"; + newscript->pGossipHello = &GossipHello_guard_tirisfal; + newscript->pGossipSelect = &GossipSelect_guard_tirisfal; + newscript->GetAI = GetAI_guard_tirisfal; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="guard_undercity"; + newscript->pGossipHello = &GossipHello_guard_undercity; + newscript->pGossipSelect = &GossipSelect_guard_undercity; + newscript->GetAI = GetAI_guard_undercity; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/item/item_scripts.cpp b/src/bindings/scripts/scripts/item/item_scripts.cpp new file mode 100644 index 00000000000..6d2bfe8d852 --- /dev/null +++ b/src/bindings/scripts/scripts/item/item_scripts.cpp @@ -0,0 +1,529 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Item_Scripts +SD%Complete: 100 +SDComment: Items for a range of different items. See content below (in script) +SDCategory: Items +EndScriptData */ + +/* ContentData +item_area_52_special(i28132) Prevents abuse of this item +item_attuned_crystal_cores(i34368) Prevent abuse(quest 11524 & 11525) +item_blackwhelp_net(i31129) Quest Whelps of the Wyrmcult (q10747). Prevents abuse +item_draenei_fishing_net(i23654) Hacklike implements chance to spawn item or creature +item_disciplinary_rod Prevents abuse +item_nether_wraith_beacon(i31742) Summons creatures for quest Becoming a Spellfire Tailor (q10832) +item_flying_machine(i34060,i34061) Engineering crafted flying machines +item_gor_dreks_ointment(i30175) Protecting Our Own(q10488) +item_muiseks_vessel Cast on creature, they must be dead(q 3123,3124,3125,3126,3127) +item_protovoltaic_magneto_collector Prevents abuse +item_razorthorn_flayer_gland Quest Discovering Your Roots (q11520) and Rediscovering Your Roots (q11521). Prevents abuse +item_tame_beast_rods(many) Prevent cast on any other creature than the intended (for all tame beast quests) +item_soul_cannon(i32825) Prevents abuse of this item +item_sparrowhawk_net(i32321) Quest To Catch A Sparrowhawk (q10987). Prevents abuse +item_voodoo_charm Provide proper error message and target(q2561) +item_vorenthals_presence(i30259) Prevents abuse of this item +item_yehkinyas_bramble(i10699) Allow cast spell on vale screecher only and remove corpse if cast sucessful (q3520) +item_zezzak_shard(i31463) Quest The eyes of Grillok (q10813). Prevents abuse +EndContentData */ + +#include "precompiled.h" +#include "SpellMgr.h" +#include "Spell.h" +#include "WorldPacket.h" + +/*##### +# item_area_52_special +#####*/ + +bool ItemUse_item_area_52_special(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if ( player->GetAreaId() == 3803 ) + { + return false; + } + else + { + player->SendEquipError(EQUIP_ERR_OUT_OF_RANGE,_Item,NULL); + return true; + } +} + +/*##### +# item_attuned_crystal_cores +#####*/ + +bool ItemUse_item_attuned_crystal_cores(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + targets.getUnitTarget()->GetEntry() == 24972 && targets.getUnitTarget()->isDead() ) + return false; + + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,_Item,NULL); + return true; +} + +/*##### +# item_blackwhelp_net +#####*/ + +bool ItemUse_item_blackwhelp_net(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + targets.getUnitTarget()->GetEntry() == 21387 ) + return false; + + player->SendEquipError(EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM,_Item,NULL); + return true; +} + +/*##### +# item_draenei_fishing_net +#####*/ + +//This is just a hack and should be removed from here. +//Creature/Item are in fact created before spell are sucessfully casted, without any checks at all to ensure proper/expected behavior. +bool ItemUse_item_draenei_fishing_net(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + //if( targets.getGOTarget() && targets.getGOTarget()->GetTypeId() == TYPEID_GAMEOBJECT && + //targets.getGOTarget()->GetGOInfo()->type == GAMEOBJECT_TYPE_SPELL_FOCUS && targets.getGOTarget()->GetEntry() == 181616 ) + //{ + if( player->GetQuestStatus(9452) == QUEST_STATUS_INCOMPLETE ) + { + if( rand()%100 < 35 ) + { + Creature *Murloc = player->SummonCreature(17102,player->GetPositionX() ,player->GetPositionY()+20, player->GetPositionZ(), 0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + if( Murloc ) + Murloc->AI()->AttackStart(player); + } + else + { + ItemPosCountVec dest; + uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 23614, 1); + if( msg == EQUIP_ERR_OK ) + { + Item* item = player->StoreNewItem(dest,23614,true); + if( item ) + player->SendNewItem(item,1,false,true); + }else + player->SendEquipError(msg,NULL,NULL); + } + } + //} + return false; +} + +/*##### +# item_disciplinary_rod +#####*/ + +bool ItemUse_item_disciplinary_rod(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + (targets.getUnitTarget()->GetEntry() == 15941 || targets.getUnitTarget()->GetEntry() == 15945) ) + return false; + + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,_Item,NULL); + return true; +} + +/*##### +# item_nether_wraith_beacon +#####*/ + +bool ItemUse_item_nether_wraith_beacon(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if (player->GetQuestStatus(10832) == QUEST_STATUS_INCOMPLETE) + { + Creature *Nether; + Nether = player->SummonCreature(22408,player->GetPositionX() ,player->GetPositionY()+20, player->GetPositionZ(), 0,TEMPSUMMON_TIMED_DESPAWN,180000); + Nether = player->SummonCreature(22408,player->GetPositionX() ,player->GetPositionY()-20, player->GetPositionZ(), 0,TEMPSUMMON_TIMED_DESPAWN,180000); + if (Nether) + ((CreatureAI*)Nether->AI())->AttackStart(player); + } + return false; +} + +/*##### +# item_flying_machine +#####*/ + +bool ItemUse_item_flying_machine(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + uint32 itemId = _Item->GetEntry(); + if( itemId == 34060 ) + if( player->GetBaseSkillValue(SKILL_RIDING) >= 225 ) + return false; + + if( itemId == 34061 ) + if( player->GetBaseSkillValue(SKILL_RIDING) == 300 ) + return false; + + debug_log("SD2: Player attempt to use item %u, but did not meet riding requirement",itemId); + player->SendEquipError(EQUIP_ERR_ERR_CANT_EQUIP_SKILL,_Item,NULL); + return true; +} + +/*##### +# item_gor_dreks_ointment +#####*/ + +bool ItemUse_item_gor_dreks_ointment(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + targets.getUnitTarget()->GetEntry() == 20748 && !targets.getUnitTarget()->HasAura(32578,0) ) + return false; + + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,_Item,NULL); + return true; +} + +/*##### +# item_muiseks_vessel +#####*/ + +bool ItemUse_item_muiseks_vessel(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + Unit* uTarget = targets.getUnitTarget(); + uint32 itemSpell = _Item->GetProto()->Spells[0].SpellId; + uint32 cEntry = 0; + uint32 cEntry2 = 0; + uint32 cEntry3 = 0; + uint32 cEntry4 = 0; + + if(itemSpell) + { + switch(itemSpell) + { + case 11885: //Wandering Forest Walker + cEntry = 7584; + break; + case 11886: //Owlbeasts + cEntry = 2927; + cEntry2 = 2928; + cEntry3 = 2929; + cEntry4 = 7808; + break; + case 11887: //Freyfeather Hippogryphs + cEntry = 5300; + cEntry2 = 5304; + cEntry3 = 5305; + cEntry4 = 5306; + break; + case 11888: //Sprite Dragon Sprite Darters + cEntry = 5276; + cEntry2 = 5278; + break; + case 11889: //Zapped Land Walker Land Walker Zapped Cliff Giant Cliff Giant + cEntry = 5357; + cEntry2 = 5358; + cEntry3 = 14640; + cEntry4 = 14604; + break; + } + if( uTarget && uTarget->GetTypeId()==TYPEID_UNIT && uTarget->isDead() && + (uTarget->GetEntry()==cEntry || uTarget->GetEntry()==cEntry2 || uTarget->GetEntry()==cEntry3 || uTarget->GetEntry()==cEntry4) ) + { + ((Creature*)uTarget)->RemoveCorpse(); + return false; + } + } + + WorldPacket data(SMSG_CAST_FAILED, (4+2)); // prepare packet error message + data << uint32(_Item->GetEntry()); // itemId + data << uint8(SPELL_FAILED_BAD_TARGETS); // reason + player->GetSession()->SendPacket(&data); // send message: Invalid target + + player->SendEquipError(EQUIP_ERR_NONE,_Item,NULL); // break spell + return true; +} + +/*##### +# item_razorthorn_flayer_gland +#####*/ + +bool ItemUse_item_razorthorn_flayer_gland(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + targets.getUnitTarget()->GetEntry() == 24922 ) + return false; + + player->SendEquipError(EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM,_Item,NULL); + return true; +} + +/*##### +# item_tame_beast_rods +#####*/ + +bool ItemUse_item_tame_beast_rods(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + uint32 itemSpell = _Item->GetProto()->Spells[0].SpellId; + uint32 cEntry = 0; + + if(itemSpell) + { + switch(itemSpell) + { + case 19548: cEntry = 1196; break; //Ice Claw Bear + case 19674: cEntry = 1126; break; //Large Crag Boar + case 19687: cEntry = 1201; break; //Snow Leopard + case 19688: cEntry = 2956; break; //Adult Plainstrider + case 19689: cEntry = 2959; break; //Prairie Stalker + case 19692: cEntry = 2970; break; //Swoop + case 19693: cEntry = 1998; break; //Webwood Lurker + case 19694: cEntry = 3099; break; //Dire Mottled Boar + case 19696: cEntry = 3107; break; //Surf Crawler + case 19697: cEntry = 3126; break; //Armored Scorpid + case 19699: cEntry = 2043; break; //Nightsaber Stalker + case 19700: cEntry = 1996; break; //Strigid Screecher + case 30646: cEntry = 17217; break; //Barbed Crawler + case 30653: cEntry = 17374; break; //Greater Timberstrider + case 30654: cEntry = 17203; break; //Nightstalker + case 30099: cEntry = 15650; break; //Crazed Dragonhawk + case 30102: cEntry = 15652; break; //Elder Springpaw + case 30105: cEntry = 16353; break; //Mistbat + } + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + targets.getUnitTarget()->GetEntry() == cEntry ) + return false; + } + + WorldPacket data(SMSG_CAST_FAILED, (4+2)); // prepare packet error message + data << uint32(_Item->GetEntry()); // itemId + data << uint8(SPELL_FAILED_BAD_TARGETS); // reason + player->GetSession()->SendPacket(&data); // send message: Invalid target + + player->SendEquipError(EQUIP_ERR_NONE,_Item,NULL); // break spell + return true; +} + +/*##### +# item_protovoltaic_magneto_collector +#####*/ + +bool ItemUse_item_protovoltaic_magneto_collector(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + targets.getUnitTarget()->GetEntry() == 21729 ) + return false; + + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,_Item,NULL); + return true; +} + +/*##### +# item_soul_cannon +#####*/ + +bool ItemUse_item_soul_cannon(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + // allow use + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + targets.getUnitTarget()->GetEntry() == 22357 ) + return false; + + // error + player->SendEquipError(EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM,_Item,NULL); + return true; +} + +/*##### +# item_sparrowhawk_net +#####*/ + +bool ItemUse_item_sparrowhawk_net(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + targets.getUnitTarget()->GetEntry() == 22979 ) + return false; + + player->SendEquipError(EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM,_Item,NULL); + return true; +} + +/*##### +# item_voodoo_charm +#####*/ + +bool ItemUse_item_voodoo_charm(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && targets.getUnitTarget()->isDead() && + targets.getUnitTarget()->GetEntry()==7318 ) + return false; + + WorldPacket data(SMSG_CAST_FAILED, (4+2)); // prepare packet error message + data << uint32(_Item->GetEntry()); // itemId + data << uint8(SPELL_FAILED_BAD_TARGETS); // reason + player->GetSession()->SendPacket(&data); // send message: Invalid target + + player->SendEquipError(EQUIP_ERR_NONE,_Item,NULL); // break spell + return true; +} + +/*##### +# item_vorenthals_presence +#####*/ + +bool ItemUse_item_vorenthals_presence(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + // allow use + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + targets.getUnitTarget()->GetEntry() == 20132 ) + return false; + + // error + player->SendEquipError(EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM,_Item,NULL); + return true; +} + +/*##### +# item_yehkinyas_bramble +#####*/ + +bool ItemUse_item_yehkinyas_bramble(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if (player->GetQuestStatus(3520) == QUEST_STATUS_INCOMPLETE) + { + Unit * unit_target = targets.getUnitTarget(); + if( unit_target && + unit_target->GetTypeId()==TYPEID_UNIT && + unit_target->isDead() && + // cast only on corpse 5307 or 5308 + (unit_target->GetEntry()==5307 || unit_target->GetEntry()==5308) ) + { + ((Creature*)unit_target)->RemoveCorpse(); // remove corpse for cancelling second use + return false; // all ok + } + } + WorldPacket data(SMSG_CAST_FAILED, (4+2)); // prepare packet error message + data << uint32(10699); // itemId + data << uint8(SPELL_FAILED_BAD_TARGETS); // reason + player->GetSession()->SendPacket(&data); // send message: Bad target + player->SendEquipError(EQUIP_ERR_NONE,_Item,NULL); // break spell + return true; +} + +/*##### +# item_zezzak_shard +#####*/ + +bool ItemUse_item_zezzak_shard(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + if( targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT && + targets.getUnitTarget()->GetEntry() == 19440 ) + return false; + + player->SendEquipError(EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM,_Item,NULL); + return true; +} + +void AddSC_item_scripts() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="item_area_52_special"; + newscript->pItemUse = ItemUse_item_area_52_special; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_attuned_crystal_cores"; + newscript->pItemUse = ItemUse_item_attuned_crystal_cores; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_blackwhelp_net"; + newscript->pItemUse = ItemUse_item_blackwhelp_net; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_disciplinary_rod"; + newscript->pItemUse = ItemUse_item_disciplinary_rod; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_draenei_fishing_net"; + newscript->pItemUse = ItemUse_item_draenei_fishing_net; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_nether_wraith_beacon"; + newscript->pItemUse = ItemUse_item_nether_wraith_beacon; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_flying_machine"; + newscript->pItemUse = ItemUse_item_flying_machine; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_gor_dreks_ointment"; + newscript->pItemUse = ItemUse_item_gor_dreks_ointment; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_muiseks_vessel"; + newscript->pItemUse = ItemUse_item_muiseks_vessel; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_razorthorn_flayer_gland"; + newscript->pItemUse = ItemUse_item_razorthorn_flayer_gland; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_tame_beast_rods"; + newscript->pItemUse = ItemUse_item_tame_beast_rods; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_protovoltaic_magneto_collector"; + newscript->pItemUse = ItemUse_item_protovoltaic_magneto_collector; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_soul_cannon"; + newscript->pItemUse = ItemUse_item_soul_cannon; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_sparrowhawk_net"; + newscript->pItemUse = ItemUse_item_sparrowhawk_net; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_voodoo_charm"; + newscript->pItemUse = ItemUse_item_voodoo_charm; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_vorenthals_presence"; + newscript->pItemUse = ItemUse_item_vorenthals_presence; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_yehkinyas_bramble"; + newscript->pItemUse = ItemUse_item_yehkinyas_bramble; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_zezzaks_shard"; + newscript->pItemUse = ItemUse_item_zezzak_shard; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/item/item_test.cpp b/src/bindings/scripts/scripts/item/item_test.cpp new file mode 100644 index 00000000000..e4f7285e52a --- /dev/null +++ b/src/bindings/scripts/scripts/item/item_test.cpp @@ -0,0 +1,42 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Item_Test +SD%Complete: 100 +SDComment: Used for Testing Item Scripts +SDCategory: Items +EndScriptData */ + +#include "precompiled.h" + +extern void LoadDatabase(); + +bool ItemUse_item_test(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + LoadDatabase(); + return true; +} + +void AddSC_item_test() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="item_test"; + newscript->pItemUse = ItemUse_item_test; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/npc/npc_escortAI.cpp b/src/bindings/scripts/scripts/npc/npc_escortAI.cpp new file mode 100644 index 00000000000..d846727ac0a --- /dev/null +++ b/src/bindings/scripts/scripts/npc/npc_escortAI.cpp @@ -0,0 +1,304 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +/* ScriptData +SDName: Npc_EscortAI +SD%Complete: 100 +SDComment: +SDCategory: Npc +EndScriptData */ + +#include "precompiled.h" +#include "npc_escortAI.h" + +#define WP_LAST_POINT -1 +#define MAX_PLAYER_DISTANCE 50 + +bool npc_escortAI::IsVisible(Unit* who) const +{ + if (!who) + return false; + + return (m_creature->GetDistance(who) < VISIBLE_RANGE) && who->isVisibleForOrDetect(m_creature,true); +} + +void npc_escortAI::AttackStart(Unit *who) +{ + if (!who) + return; + + if (IsBeingEscorted && !Defend) + return; + + if (who->isTargetableForAttack()) + { + //Begin attack + if ( m_creature->Attack(who, true) ) + { + m_creature->GetMotionMaster()->MovementExpired(); + m_creature->GetMotionMaster()->MoveChase(who); + m_creature->AddThreat(who, 0.0f); + } + + if (!InCombat) + { + InCombat = true; + + //Store last position + m_creature->GetPosition(LastPos.x, LastPos.y, LastPos.z); + + debug_log("SD2: EscortAI has entered combat via Attack and stored last location"); + + Aggro(who); + } + } +} + +void npc_escortAI::MoveInLineOfSight(Unit *who) +{ + if (IsBeingEscorted && !Attack) + return; + + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if (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) ) + { + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + //Begin attack + if ( m_creature->Attack(who, true) ) + { + m_creature->GetMotionMaster()->MovementExpired(); + m_creature->GetMotionMaster()->MoveChase(who); + m_creature->AddThreat(who, 0.0f); + } + + if (!InCombat) + { + InCombat = true; + + //Store last position + m_creature->GetPosition(LastPos.x, LastPos.y, LastPos.z); + debug_log("SD2: EscortAI has entered combat via LOS and stored last location"); + + Aggro(who); + } + } + } +} + +void npc_escortAI::JustRespawned() +{ + InCombat = false; + IsBeingEscorted = false; + IsOnHold = false; + + //Re-Enable gossip + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + Reset(); +} + +void npc_escortAI::EnterEvadeMode() +{ + InCombat = false; + + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(); + m_creature->SetLootRecipient(NULL); + + if (IsBeingEscorted) + { + debug_log("SD2: EscortAI has left combat and is now returning to last point"); + Returning = true; + m_creature->GetMotionMaster()->MovementExpired(); + m_creature->GetMotionMaster()->MovePoint(WP_LAST_POINT, LastPos.x, LastPos.y, LastPos.z); + + }else + { + m_creature->GetMotionMaster()->MovementExpired(); + m_creature->GetMotionMaster()->MoveTargetedHome(); + } + + Reset(); +} + +void npc_escortAI::UpdateAI(const uint32 diff) +{ + //Waypoint Updating + if (IsBeingEscorted && !InCombat && WaitTimer && !Returning) + if (WaitTimer <= diff) + { + if (ReconnectWP) + { + //Correct movement speed + if (Run) + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + else m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + + //Continue with waypoints + if( !IsOnHold ) + { + m_creature->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z ); + debug_log("SD2: EscortAI Reconnect WP is: %d, %f, %f, %f", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z); + WaitTimer = 0; + ReconnectWP = false; + return; + } + } + + //End of the line, Despawn self then immediatly respawn + if (CurrentWP == WaypointList.end()) + { + debug_log("SD2: EscortAI reached end of waypoints"); + + m_creature->setDeathState(JUST_DIED); + m_creature->SetHealth(0); + m_creature->CombatStop(); + m_creature->DeleteThreatList(); + m_creature->Respawn(); + m_creature->GetMotionMaster()->Clear(true); + + //Re-Enable gossip + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + IsBeingEscorted = false; + WaitTimer = 0; + return; + } + + if( !IsOnHold ) + { + m_creature->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z ); + debug_log("SD2: EscortAI Next WP is: %d, %f, %f, %f", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z); + WaitTimer = 0; + } + }else WaitTimer -= diff; + + //Check if player is within range + if (IsBeingEscorted && !InCombat && PlayerGUID) + if (PlayerTimer < diff) + { + Unit* p = Unit::GetUnit(*m_creature, PlayerGUID); + + if (!p || m_creature->GetDistance(p) > MAX_PLAYER_DISTANCE) + { + JustDied(m_creature); + IsBeingEscorted = false; + + debug_log("SD2: EscortAI Evaded back to spawn point because player was to far away or not found"); + + m_creature->setDeathState(JUST_DIED); + m_creature->SetHealth(0); + m_creature->CombatStop(); + m_creature->DeleteThreatList(); + m_creature->Respawn(); + m_creature->GetMotionMaster()->Clear(true); + + //Re-Enable gossip + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + + PlayerTimer = 1000; + }else PlayerTimer -= diff; + + //Check if we have a current target + if( m_creature->isAlive() && m_creature->SelectHostilTarget() && m_creature->getVictim()) + { + //If we are within range melee the target + if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + { + if( m_creature->isAttackReady() ) + { + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); + } + } + } +} + +void npc_escortAI::MovementInform(uint32 type, uint32 id) +{ + if (type != POINT_MOTION_TYPE || !IsBeingEscorted) + return; + + //Original position reached, continue waypoint movement + if (id == WP_LAST_POINT) + { + debug_log("SD2: EscortAI has returned to original position before combat"); + ReconnectWP = true; + Returning = false; + WaitTimer = 1; + + }else + { + //Make sure that we are still on the right waypoint + if (CurrentWP->id != id) + { + debug_log("SD2 ERROR: EscortAI reached waypoint out of order %d, expected %d", id, CurrentWP->id); + return; + } + + debug_log("SD2: EscortAI Waypoint %d reached", CurrentWP->id); + + //Call WP function + WaypointReached(CurrentWP->id); + + WaitTimer = CurrentWP->WaitTimeMs + 1; + + ++CurrentWP; + } +} + +void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 WaitTimeMs) +{ + Escort_Waypoint t(id, x, y, z, WaitTimeMs); + + WaypointList.push_back(t); +} + +void npc_escortAI::Start(bool bAttack, bool bDefend, bool bRun, uint64 pGUID) +{ + if (InCombat) + { + debug_log("SD2 ERROR: EscortAI attempt to Start while in combat"); + return; + } + + if (WaypointList.empty()) + { + debug_log("SD2 ERROR: Call to escortAI::Start with 0 waypoints"); + return; + } + + Attack = bAttack; + Defend = bDefend; + Run = bRun; + PlayerGUID = pGUID; + + debug_log("SD2: EscortAI started with %d waypoints. Attack = %d, Defend = %d, Run = %d, PlayerGUID = %d", WaypointList.size(), Attack, Defend, Run, PlayerGUID); + + CurrentWP = WaypointList.begin(); + + //Set initial speed + if (Run) + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + else m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + + //Start WP + m_creature->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z ); + debug_log("SD2: EscortAI Next WP is: %d, %f, %f, %f", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z); + IsBeingEscorted = true; + ReconnectWP = false; + Returning = false; + IsOnHold = false; + + //Disable gossip + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); +} diff --git a/src/bindings/scripts/scripts/npc/npc_escortAI.h b/src/bindings/scripts/scripts/npc/npc_escortAI.h new file mode 100644 index 00000000000..e1dbf186764 --- /dev/null +++ b/src/bindings/scripts/scripts/npc/npc_escortAI.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_ESCORTAI_H +#define SC_ESCORTAI_H + +struct Escort_Waypoint +{ + Escort_Waypoint(uint32 _id, float _x, float _y, float _z, uint32 _w) + { + id = _id; + x = _x; + y = _y; + z = _z; + WaitTimeMs = _w; + } + + uint32 id; + float x; + float y; + float z; + uint32 WaitTimeMs; +}; + +struct MANGOS_DLL_DECL npc_escortAI : public ScriptedAI +{ + public: + + // Pure Virtual Functions + virtual void WaypointReached(uint32) = 0; + + virtual void Aggro(Unit*) = 0; + + virtual void Reset() = 0; + + // CreatureAI functions + npc_escortAI(Creature *c) : ScriptedAI(c), IsBeingEscorted(false), PlayerTimer(1000) {m_creature->GetPosition(LastPos.x, LastPos.y, LastPos.z);} + + bool IsVisible(Unit*) const; + + void AttackStart(Unit*); + + void MoveInLineOfSight(Unit*); + + void JustRespawned(); + + void EnterEvadeMode(); + + void UpdateAI(const uint32); + + void MovementInform(uint32, uint32); + + // EscortAI functions + void AddWaypoint(uint32 id, float x, float y, float z, uint32 WaitTimeMs = 0); + + void Start(bool bAttack, bool bDefend, bool bRun, uint64 pGUID = 0); + + // EscortAI variables + protected: + uint64 PlayerGUID; + bool IsBeingEscorted; + bool IsOnHold; + + private: + uint32 WaitTimer; + uint32 PlayerTimer; + + struct + { + float x; + float y; + float z; + }LastPos; + + std::list WaypointList; + std::list::iterator CurrentWP; + + bool Attack; + bool Defend; + bool Returning; + bool ReconnectWP; + bool Run; +}; +#endif diff --git a/src/bindings/scripts/scripts/npc/npc_innkeeper.cpp b/src/bindings/scripts/scripts/npc/npc_innkeeper.cpp new file mode 100644 index 00000000000..429d5b51115 --- /dev/null +++ b/src/bindings/scripts/scripts/npc/npc_innkeeper.cpp @@ -0,0 +1,144 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Npc_Innkeeper +SD%Complete: 50 +SDComment: This script are currently not in use. EventSystem cannot be used on Windows build of SD2 +SDCategory: NPCs +EndScriptData */ + +#include "precompiled.h" + +#define HALLOWEEN_EVENTID 12 +#define SPELL_TRICK_OR_TREATED 24755 +#define SPELL_TREAT 24715 + +#define LOCALE_TRICK_OR_TREAT_0 "Trick or Treat!" +#define LOCALE_TRICK_OR_TREAT_2 "Des bonbons ou des blagues!" +#define LOCALE_TRICK_OR_TREAT_3 "Süßes oder Saures!" +#define LOCALE_TRICK_OR_TREAT_6 "¡Truco o trato!" + +bool isEventActive() +{ + /* + const GameEvent::ActiveEvents *ActiveEventsList = gameeventmgr.GetActiveEventList(); + GameEvent::ActiveEvents::const_iterator itr; + for (itr = ActiveEventsList->begin(); itr != ActiveEventsList->end(); ++itr) + { + if (*itr==HALLOWEEN_EVENTID) + { + return true; + } + }*/ + return false; +} + +bool GossipHello_npc_innkeeper(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (isEventActive()&& !player->GetAura(SPELL_TRICK_OR_TREATED,0)) + { + char* localizedEntry; + switch (player->GetSession()->GetSessionDbLocaleIndex()) + { + case 0: + localizedEntry=LOCALE_TRICK_OR_TREAT_0; + break; + case 2: + localizedEntry=LOCALE_TRICK_OR_TREAT_2; + break; + case 3: + localizedEntry=LOCALE_TRICK_OR_TREAT_3; + break; + case 6: + localizedEntry=LOCALE_TRICK_OR_TREAT_6; + break; + default: + localizedEntry=LOCALE_TRICK_OR_TREAT_0; + } + + player->ADD_GOSSIP_ITEM(0, localizedEntry, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+HALLOWEEN_EVENTID); + } + + player->TalkedToCreature(_Creature->GetEntry(),_Creature->GetGUID()); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_innkeeper(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF+HALLOWEEN_EVENTID && isEventActive() && !player->GetAura(SPELL_TRICK_OR_TREATED,0)) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player, SPELL_TRICK_OR_TREATED, true); + + // either trick or treat, 50% chance + if(rand()%2) + { + player->CastSpell(player, SPELL_TREAT, true); + } + else + { + int32 trickspell=0; + switch (rand()%9) // note that female characters can get male costumes and vice versa + { + case 0: + trickspell=24753; // cannot cast, random 30sec + break; + case 1: + trickspell=24713; // lepper gnome costume + break; + case 2: + trickspell=24735; // male ghost costume + break; + case 3: + trickspell=24736; // female ghostcostume + break; + case 4: + trickspell=24710; // male ninja costume + break; + case 5: + trickspell=24711; // female ninja costume + break; + case 6: + trickspell=24708; // male pirate costume + break; + case 7: + trickspell=24709; // female pirate costume + break; + case 8: + trickspell=24723; // skeleton costume + break; + } + player->CastSpell(player, trickspell, true); + } + return true; // prevent mangos core handling + } + return false; // the player didn't select "trick or treat" or cheated, normal core handling +} + +void AddSC_npc_innkeeper() +{ + Script *newscript; + newscript = new Script; + newscript->Name="npc_innkeeper"; + newscript->pGossipHello = &GossipHello_npc_innkeeper; + newscript->pGossipSelect = &GossipSelect_npc_innkeeper; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/npc/npc_professions.cpp b/src/bindings/scripts/scripts/npc/npc_professions.cpp new file mode 100644 index 00000000000..65487786058 --- /dev/null +++ b/src/bindings/scripts/scripts/npc/npc_professions.cpp @@ -0,0 +1,1205 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Npc_Professions +SD%Complete: 80 +SDComment: Provides learn/unlearn/relearn-options for professions. Not supported: Unlearn engineering, re-learn engineering, re-learn leatherworking. +SDCategory: NPCs +EndScriptData */ + +#include "precompiled.h" + +/* +A few notes for future developement: +- A full implementation of gossip for GO's is required. They must have the same scripting capabilities as creatures. Basically, +there is no difference here (except that default text is chosen with `gameobject_template`.`data3` (for GO type2, different dataN for a few others) +- It's possible blacksmithing still require some tweaks and adjustments due to the way we _have_ to use reputation. +*/ + +/* +-- UPDATE `gameobject_template` SET `ScriptName` = 'go_soothsaying_for_dummies' WHERE `entry` = 177226; +*/ + +/*### +# to be removed from here (->ncp_text). This is data for database projects. +###*/ +#define TALK_MUST_UNLEARN_WEAPON "You must forget your weapon type specialty before I can help you. Go to Everlook in Winterspring and seek help there." + +#define TALK_HAMMER_LEARN "Ah, a seasoned veteran you once were. I know you are capable, you merely need to ask and I shall teach you the way of the hammersmith." +#define TALK_AXE_LEARN "Ah, a seasoned veteran you once were. I know you are capable, you merely need to ask and I shall teach you the way of the axesmith." +#define TALK_SWORD_LEARN "Ah, a seasoned veteran you once were. I know you are capable, you merely need to ask and I shall teach you the way of the swordsmith." + +#define TALK_HAMMER_UNLEARN "Forgetting your Hammersmithing skill is not something to do lightly. If you choose to abandon it you will forget all recipes that require Hammersmithing to create!" +#define TALK_AXE_UNLEARN "Forgetting your Axesmithing skill is not something to do lightly. If you choose to abandon it you will forget all recipes that require Axesmithing to create!" +#define TALK_SWORD_UNLEARN "Forgetting your Swordsmithing skill is not something to do lightly. If you choose to abandon it you will forget all recipes that require Swordsmithing to create!" + +/*### +# generic defines +###*/ + +#define GOSSIP_SENDER_LEARN 50 +#define GOSSIP_SENDER_UNLEARN 51 +#define GOSSIP_SENDER_CHECK 52 + +/*### +# gossip item and box texts +###*/ + +#define GOSSIP_LEARN_POTION "Please teach me how to become a Master of Potions, Lauranna" +#define GOSSIP_UNLEARN_POTION "I wish to unlearn Potion Mastery" +#define GOSSIP_LEARN_TRANSMUTE "Please teach me how to become a Master of Transmutations, Zarevhi" +#define GOSSIP_UNLEARN_TRANSMUTE "I wish to unlearn Transmutation Mastery" +#define GOSSIP_LEARN_ELIXIR "Please teach me how to become a Master of Elixirs, Lorokeem" +#define GOSSIP_UNLEARN_ELIXIR "I wish to unlearn Elixir Mastery" + +#define BOX_UNLEARN_ALCHEMY_SPEC "Do you really want to unlearn your alchemy specialty and lose all associated recipes? \n Cost: " + +#define GOSSIP_WEAPON_LEARN "Please teach me how to become a Weaponsmith" +#define GOSSIP_WEAPON_UNLEARN "I wish to unlearn the art of Weaponsmithing" +#define GOSSIP_ARMOR_LEARN "Please teach me how to become a Armorsmith" +#define GOSSIP_ARMOR_UNLEARN "I wish to unlearn the art of Armorsmithing" + +#define GOSSIP_UNLEARN_SMITH_SPEC "I wish to unlearn my blacksmith specialty" +#define BOX_UNLEARN_ARMORORWEAPON "Do you really want to unlearn your blacksmith specialty and lose all associated recipes? \n Cost: " + +#define GOSSIP_LEARN_HAMMER "Please teach me how to become a Hammersmith, Lilith" +#define GOSSIP_UNLEARN_HAMMER "I wish to unlearn Hammersmithing" +#define GOSSIP_LEARN_AXE "Please teach me how to become a Axesmith, Kilram" +#define GOSSIP_UNLEARN_AXE "I wish to unlearn Axesmithing" +#define GOSSIP_LEARN_SWORD "Please teach me how to become a Swordsmith, Seril" +#define GOSSIP_UNLEARN_SWORD "I wish to unlearn Swordsmithing" + +#define BOX_UNLEARN_WEAPON_SPEC "Do you really want to unlearn your weaponsmith specialty and lose all associated recipes? \n Cost: " + +#define GOSSIP_LEARN_DRAGON "I am absolutely certain that i want to learn dragonscale leatherworking" +#define GOSSIP_UNLEARN_DRAGON "I wish to unlearn Dragonscale Leatherworking" +#define GOSSIP_LEARN_ELEMENTAL "I am absolutely certain that i want to learn elemental leatherworking" +#define GOSSIP_UNLEARN_ELEMENTAL "I wish to unlearn Elemental Leatherworking" +#define GOSSIP_LEARN_TRIBAL "I am absolutely certain that i want to learn tribal leatherworking" +#define GOSSIP_UNLEARN_TRIBAL "I wish to unlearn Tribal Leatherworking" + +#define BOX_UNLEARN_LEATHER_SPEC "Do you really want to unlearn your leatherworking specialty and lose all associated recipes? \n Cost: " + +#define GOSSIP_LEARN_SPELLFIRE "Please teach me how to become a Spellcloth tailor" +#define GOSSIP_UNLEARN_SPELLFIRE "I wish to unlearn Spellfire Tailoring" +#define GOSSIP_LEARN_MOONCLOTH "Please teach me how to become a Mooncloth tailor" +#define GOSSIP_UNLEARN_MOONCLOTH "I wish to unlearn Mooncloth Tailoring" +#define GOSSIP_LEARN_SHADOWEAVE "Please teach me how to become a Shadoweave tailor" +#define GOSSIP_UNLEARN_SHADOWEAVE "I wish to unlearn Shadoweave Tailoring" + +#define BOX_UNLEARN_TAILOR_SPEC "Do you really want to unlearn your tailoring specialty and lose all associated recipes? \n Cost: " + +#define GOSSIP_LEARN_GOBLIN "I am absolutely certain that i want to learn Goblin engineering" +#define GOSSIP_LEARN_GNOMISH "I am absolutely certain that i want to learn Gnomish engineering" + +/*### +# spells defines +###*/ + +#define S_WEAPON 9787 +#define S_ARMOR 9788 +#define S_HAMMER 17040 +#define S_AXE 17041 +#define S_SWORD 17039 + +#define S_LEARN_WEAPON 9789 +#define S_LEARN_ARMOR 9790 +#define S_LEARN_HAMMER 39099 +#define S_LEARN_AXE 39098 +#define S_LEARN_SWORD 39097 + +#define S_UNLEARN_WEAPON 36436 +#define S_UNLEARN_ARMOR 36435 +#define S_UNLEARN_HAMMER 36441 +#define S_UNLEARN_AXE 36439 +#define S_UNLEARN_SWORD 36438 + +#define S_REP_ARMOR 17451 +#define S_REP_WEAPON 17452 + +#define REP_ARMOR 46 +#define REP_WEAPON 289 +#define REP_HAMMER 569 +#define REP_AXE 570 +#define REP_SWORD 571 + +#define S_DRAGON 10656 +#define S_ELEMENTAL 10658 +#define S_TRIBAL 10660 + +#define S_LEARN_DRAGON 10657 +#define S_LEARN_ELEMENTAL 10659 +#define S_LEARN_TRIBAL 10661 + +#define S_UNLEARN_DRAGON 36434 +#define S_UNLEARN_ELEMENTAL 36328 +#define S_UNLEARN_TRIBAL 36433 + +#define S_GOBLIN 20222 +#define S_GNOMISH 20219 + +#define S_LEARN_GOBLIN 20221 +#define S_LEARN_GNOMISH 20220 + +#define S_SPELLFIRE 26797 +#define S_MOONCLOTH 26798 +#define S_SHADOWEAVE 26801 + +#define S_LEARN_SPELLFIRE 26796 +#define S_LEARN_MOONCLOTH 26799 +#define S_LEARN_SHADOWEAVE 26800 + +#define S_UNLEARN_SPELLFIRE 41299 +#define S_UNLEARN_MOONCLOTH 41558 +#define S_UNLEARN_SHADOWEAVE 41559 + +#define S_TRANSMUTE 28672 +#define S_ELIXIR 28677 +#define S_POTION 28675 + +#define S_LEARN_TRANSMUTE 28674 +#define S_LEARN_ELIXIR 28678 +#define S_LEARN_POTION 28676 + +#define S_UNLEARN_TRANSMUTE 41565 +#define S_UNLEARN_ELIXIR 41564 +#define S_UNLEARN_POTION 41563 + +/*### +# formulas to calculate unlearning cost +###*/ + +int32 DoLearnCost(Player *player) //tailor, alchemy +{ + return 200000; +} + +int32 DoHighUnlearnCost(Player *player) //tailor, alchemy +{ + return 1500000; +} + +int32 DoMedUnlearnCost(Player *player) //blacksmith, leatherwork +{ + uint32 level = player->getLevel(); + if(level < 51) + return 250000; + else if (level < 66) + return 500000; + else + return 1000000; +} + +int32 DoLowUnlearnCost(Player *player) //blacksmith +{ + uint32 level = player->getLevel(); + if (level < 66) + return 50000; + else + return 100000; +} + +/*### +# unlearning related profession spells +###*/ + +bool EquippedOk(Player* player, uint32 spellId) +{ + SpellEntry const* spell = GetSpellStore()->LookupEntry(spellId); + + if( !spell ) + return false; + + for(int i=0; i<3; i++) + { + uint32 reqSpell = spell->EffectTriggerSpell[i]; + if( !reqSpell ) + continue; + + Item* pItem; + for(int j = EQUIPMENT_SLOT_START; j < EQUIPMENT_SLOT_END; j++) + { + pItem = player->GetItemByPos( INVENTORY_SLOT_BAG_0, j ); + if( pItem ) + if( pItem->GetProto()->RequiredSpell == reqSpell ) + { + //player has item equipped that require specialty. Not allow to unlearn, player has to unequip first + debug_log("SD2: player attempt to unlearn spell %u, but item %u is equipped.",reqSpell,pItem->GetProto()->ItemId); + return false; + } + } + } + return true; +} + +void ProfessionUnlearnSpells(Player *player, uint32 type) +{ + switch (type) + { + case 36436: // S_UNLEARN_WEAPON + player->removeSpell(36125); // Light Earthforged Blade + player->removeSpell(36128); // Light Emberforged Hammer + player->removeSpell(36126); // Light Skyforged Axe + break; + case 36435: // S_UNLEARN_ARMOR + player->removeSpell(36122); // Earthforged Leggings + player->removeSpell(36129); // Heavy Earthforged Breastplate + player->removeSpell(36130); // Stormforged Hauberk + player->removeSpell(34533); // Breastplate of Kings + player->removeSpell(34529); // Nether Chain Shirt + player->removeSpell(34534); // Bulwark of Kings + player->removeSpell(36257); // Bulwark of the Ancient Kings + player->removeSpell(36256); // Embrace of the Twisting Nether + player->removeSpell(34530); // Twisting Nether Chain Shirt + player->removeSpell(36124); // Windforged Leggings + break; + case 36441: // S_UNLEARN_HAMMER + player->removeSpell(36262); // Dragonstrike + player->removeSpell(34546); // Dragonmaw + player->removeSpell(34545); // Drakefist Hammer + player->removeSpell(36136); // Lavaforged Warhammer + player->removeSpell(34547); // Thunder + player->removeSpell(34567); // Deep Thunder + player->removeSpell(36263); // Stormherald + player->removeSpell(36137); // Great Earthforged Hammer + break; + case 36439: // S_UNLEARN_AXE + player->removeSpell(36260); // Wicked Edge of the Planes + player->removeSpell(34562); // Black Planar Edge + player->removeSpell(34541); // The Planar Edge + player->removeSpell(36134); // Stormforged Axe + player->removeSpell(36135); // Skyforged Great Axe + player->removeSpell(36261); // Bloodmoon + player->removeSpell(34543); // Lunar Crescent + player->removeSpell(34544); // Mooncleaver + break; + case 36438: // S_UNLEARN_SWORD + player->removeSpell(36258); // Blazefury + player->removeSpell(34537); // Blazeguard + player->removeSpell(34535); // Fireguard + player->removeSpell(36131); // Windforged Rapier + player->removeSpell(36133); // Stoneforged Claymore + player->removeSpell(34538); // Lionheart Blade + player->removeSpell(34540); // Lionheart Champion + player->removeSpell(36259); // Lionheart Executioner + break; + case 36434: // S_UNLEARN_DRAGON + player->removeSpell(36076); // Dragonstrike Leggings + player->removeSpell(36079); // Golden Dragonstrike Breastplate + player->removeSpell(35576); // Ebon Netherscale Belt + player->removeSpell(35577); // Ebon Netherscale Bracers + player->removeSpell(35575); // Ebon Netherscale Breastplate + player->removeSpell(35582); // Netherstrike Belt + player->removeSpell(35584); // Netherstrike Bracers + player->removeSpell(35580); // Netherstrike Breastplate + break; + case 36328: // S_UNLEARN_ELEMENTAL + player->removeSpell(36074); // Blackstorm Leggings + player->removeSpell(36077); // Primalstorm Breastplate + player->removeSpell(35590); // Primalstrike Belt + player->removeSpell(35591); // Primalstrike Bracers + player->removeSpell(35589); // Primalstrike Vest + break; + case 36433: // S_UNLEARN_TRIBAL + player->removeSpell(35585); // Windhawk Hauberk + player->removeSpell(35587); // Windhawk Belt + player->removeSpell(35588); // Windhawk Bracers + player->removeSpell(36075); // Wildfeather Leggings + player->removeSpell(36078); // Living Crystal Breastplate + break; + case 41299: // S_UNLEARN_SPELLFIRE + player->removeSpell(26752); // Spellfire Belt + player->removeSpell(26753); // Spellfire Gloves + player->removeSpell(26754); // Spellfire Robe + break; + case 41558: // S_UNLEARN_MOONCLOTH + player->removeSpell(26760); // Primal Mooncloth Belt + player->removeSpell(26761); // Primal Mooncloth Shoulders + player->removeSpell(26762); // Primal Mooncloth Robe + break; + case 41559: // S_UNLEARN_SHADOWEAVE + player->removeSpell(26756); // Frozen Shadoweave Shoulders + player->removeSpell(26757); // Frozen Shadoweave Boots + player->removeSpell(26758); // Frozen Shadoweave Robe + break; + } +} + +/*### +# start menues alchemy +###*/ + +bool HasAlchemySpell(Player *player) +{ + if(player->HasSpell(S_TRANSMUTE) || player->HasSpell(S_ELIXIR) || player->HasSpell(S_POTION)) + return true; + return false; +} + +bool GossipHello_npc_prof_alchemy(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + if (_Creature->isVendor()) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + if(_Creature->isTrainer()) + player->ADD_GOSSIP_ITEM(2, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN); + + uint32 eCreature = _Creature->GetEntry(); + + if (player->HasSkill(SKILL_ALCHEMY) && player->GetBaseSkillValue(SKILL_ALCHEMY)>=350 && player->getLevel() > 67) + { + if (player->GetQuestRewardStatus(10899) || player->GetQuestRewardStatus(10902) || player->GetQuestRewardStatus(10897)) + { + switch (eCreature) + { + case 22427: //Zarevhi + if (!HasAlchemySpell(player)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_TRANSMUTE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 1); + if (player->HasSpell(S_TRANSMUTE)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_TRANSMUTE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 4); + break; + case 19052: //Lorokeem + if (!HasAlchemySpell(player)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_ELIXIR, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 2); + if (player->HasSpell(S_ELIXIR)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_ELIXIR, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 5); + break; + case 17909: //Lauranna Thar'well + if (!HasAlchemySpell(player)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_POTION, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 3); + if (player->HasSpell(S_POTION)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_POTION, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 6); + break; + } + } + } + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + return true; +} + +void SendActionMenu_npc_prof_alchemy(Player *player, Creature *_Creature, uint32 action) +{ + switch(action) + { + case GOSSIP_ACTION_TRADE: + player->SEND_VENDORLIST( _Creature->GetGUID() ); + break; + case GOSSIP_ACTION_TRAIN: + player->SEND_TRAINERLIST( _Creature->GetGUID() ); + break; + //Learn Alchemy + case GOSSIP_ACTION_INFO_DEF + 1: + if(!player->HasSpell(S_TRANSMUTE) && player->GetMoney() >= DoLearnCost(player)) + { + player->CastSpell(player, S_LEARN_TRANSMUTE, true); + player->ModifyMoney(-DoLearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + if(!player->HasSpell(S_ELIXIR) && player->GetMoney() >= DoLearnCost(player)) + { + player->CastSpell(player, S_LEARN_ELIXIR, true); + player->ModifyMoney(-DoLearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + if(!player->HasSpell(S_POTION) && player->GetMoney() >= DoLearnCost(player)) + { + player->CastSpell(player, S_LEARN_POTION, true); + player->ModifyMoney(-DoLearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + player->CLOSE_GOSSIP_MENU(); + break; + //Unlearn Alchemy + case GOSSIP_ACTION_INFO_DEF + 4: + if(player->GetMoney() >= DoHighUnlearnCost(player)) + { + _Creature->CastSpell(player, S_UNLEARN_TRANSMUTE, true); + player->ModifyMoney(-DoHighUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 5: + if(player->GetMoney() >= DoHighUnlearnCost(player)) + { + _Creature->CastSpell(player, S_UNLEARN_ELIXIR, true); + player->ModifyMoney(-DoHighUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 6: + if(player->GetMoney() >= DoHighUnlearnCost(player)) + { + _Creature->CastSpell(player, S_UNLEARN_POTION, true); + player->ModifyMoney(-DoHighUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + player->CLOSE_GOSSIP_MENU(); + break; + } +} + +void SendConfirmLearn_npc_prof_alchemy(Player *player, Creature *_Creature, uint32 action) +{ + if(action) + { + uint32 eCreature = _Creature->GetEntry(); + switch(eCreature) + { + case 22427: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_TRANSMUTE, GOSSIP_SENDER_CHECK, action); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 19052: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_ELIXIR, GOSSIP_SENDER_CHECK, action); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 17909: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_POTION, GOSSIP_SENDER_CHECK, action); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + } + } +} + +void SendConfirmUnlearn_npc_prof_alchemy(Player *player, Creature *_Creature, uint32 action) +{ + if(action) + { + uint32 eCreature = _Creature->GetEntry(); + switch(eCreature) + { + case 22427: //Zarevhi + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_TRANSMUTE, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ALCHEMY_SPEC, DoHighUnlearnCost(player)); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 19052: //Lorokeem + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_ELIXIR, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ALCHEMY_SPEC, DoHighUnlearnCost(player)); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 17909: //Lauranna Thar'well + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_POTION, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ALCHEMY_SPEC, DoHighUnlearnCost(player)); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + } + } +} + +bool GossipSelect_npc_prof_alchemy(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch(sender) + { + case GOSSIP_SENDER_MAIN: SendActionMenu_npc_prof_alchemy(player, _Creature, action); break; + case GOSSIP_SENDER_LEARN: SendConfirmLearn_npc_prof_alchemy(player, _Creature, action); break; + case GOSSIP_SENDER_UNLEARN: SendConfirmUnlearn_npc_prof_alchemy(player, _Creature, action); break; + case GOSSIP_SENDER_CHECK: SendActionMenu_npc_prof_alchemy(player, _Creature, action); break; + } + return true; +} + +/*### +# start menues blacksmith +###*/ + +bool HasWeaponSub(Player *player) +{ + if (player->HasSpell(S_HAMMER) || player->HasSpell(S_AXE) || player->HasSpell(S_SWORD)) + return true; + return false; +} + +bool GossipHello_npc_prof_blacksmith(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + if (_Creature->isVendor()) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + if(_Creature->isTrainer()) + player->ADD_GOSSIP_ITEM(2, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN); + + uint32 eCreature = _Creature->GetEntry(); + //WEAPONSMITH & ARMORSMITH + if(player->GetBaseSkillValue(SKILL_BLACKSMITHING)>=225) + { + switch (eCreature) + { + case 11145: //Myolor Sunderfury + case 11176: //Krathok Moltenfist + if(!player->HasSpell(S_ARMOR) && !player->HasSpell(S_WEAPON) && player->GetReputationRank(REP_ARMOR) == REP_FRIENDLY) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ARMOR_LEARN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + if(!player->HasSpell(S_WEAPON) && !player->HasSpell(S_ARMOR) && player->GetReputationRank(REP_WEAPON) == REP_FRIENDLY) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_WEAPON_LEARN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + break; + case 11146: //Ironus Coldsteel + case 11178: //Borgosh Corebender + if(player->HasSpell(S_WEAPON)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_WEAPON_UNLEARN, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 3); + break; + case 5164: //Grumnus Steelshaper + case 11177: //Okothos Ironrager + if(player->HasSpell(S_ARMOR)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ARMOR_UNLEARN, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 4); + break; + } + } + //WEAPONSMITH SPEC + if(player->HasSpell(S_WEAPON) && player->getLevel() > 49 && player->GetBaseSkillValue(SKILL_BLACKSMITHING)>=250) + { + switch (eCreature) + { + case 11191: //Lilith the Lithe + if(!HasWeaponSub(player)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_HAMMER, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 5); + if(player->HasSpell(S_HAMMER)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_HAMMER, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 8); + break; + case 11192: //Kilram + if(!HasWeaponSub(player)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_AXE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 6); + if(player->HasSpell(S_AXE)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_AXE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 9); + break; + case 11193: //Seril Scourgebane + if(!HasWeaponSub(player)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_SWORD, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 7); + if(player->HasSpell(S_SWORD)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_SWORD, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 10); + break; + } + } + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + return true; +} + +void SendActionMenu_npc_prof_blacksmith(Player *player, Creature *_Creature, uint32 action) +{ + switch(action) + { + case GOSSIP_ACTION_TRADE: + player->SEND_VENDORLIST( _Creature->GetGUID() ); + break; + case GOSSIP_ACTION_TRAIN: + player->SEND_TRAINERLIST( _Creature->GetGUID() ); + break; + //Learn Armor/Weapon + case GOSSIP_ACTION_INFO_DEF + 1: + if(!player->HasSpell(S_ARMOR)) + { + player->CastSpell(player, S_LEARN_ARMOR, true); + //_Creature->CastSpell(player, S_REP_ARMOR, true); + } + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + if(!player->HasSpell(S_WEAPON)) + { + player->CastSpell(player, S_LEARN_WEAPON, true); + //_Creature->CastSpell(player, S_REP_WEAPON, true); + } + player->CLOSE_GOSSIP_MENU(); + break; + //Unlearn Armor/Weapon + case GOSSIP_ACTION_INFO_DEF + 3: + if(HasWeaponSub(player)) + { + //unknown textID (TALK_MUST_UNLEARN_WEAPON) + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + //Temporary, not offilike + _Creature->MonsterSay(TALK_MUST_UNLEARN_WEAPON,0,player->GetGUID()); + } + else if( EquippedOk(player,S_UNLEARN_WEAPON) ) + { + if( player->GetMoney() >= DoLowUnlearnCost(player) ) + { + player->CastSpell(player, S_UNLEARN_WEAPON, true); + ProfessionUnlearnSpells(player, S_UNLEARN_WEAPON); + player->ModifyMoney(-DoLowUnlearnCost(player)); + _Creature->CastSpell(player, S_REP_ARMOR, true); + player->CLOSE_GOSSIP_MENU(); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } + else + { + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + } + break; + case GOSSIP_ACTION_INFO_DEF + 4: + if( EquippedOk(player,S_UNLEARN_ARMOR) ) + { + if( player->GetMoney() >= DoLowUnlearnCost(player) ) + { + player->CastSpell(player, S_UNLEARN_ARMOR, true); + ProfessionUnlearnSpells(player, S_UNLEARN_ARMOR); + player->ModifyMoney(-DoLowUnlearnCost(player)); + _Creature->CastSpell(player, S_REP_WEAPON, true); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } else + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + break; + //Learn Hammer/Axe/Sword + case GOSSIP_ACTION_INFO_DEF + 5: + player->CastSpell(player, S_LEARN_HAMMER, true); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 6: + player->CastSpell(player, S_LEARN_AXE, true); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 7: + player->CastSpell(player, S_LEARN_SWORD, true); + player->CLOSE_GOSSIP_MENU(); + break; + //Unlearn Hammer/Axe/Sword + case GOSSIP_ACTION_INFO_DEF + 8: + if( EquippedOk(player,S_UNLEARN_HAMMER) ) + { + if( player->GetMoney() >= DoMedUnlearnCost(player)) + { + player->CastSpell(player, S_UNLEARN_HAMMER, true); + ProfessionUnlearnSpells(player, S_UNLEARN_HAMMER); + player->ModifyMoney(-DoMedUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } else + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 9: + if( EquippedOk(player,S_UNLEARN_AXE) ) + { + if( player->GetMoney() >= DoMedUnlearnCost(player)) + { + player->CastSpell(player, S_UNLEARN_AXE, true); + ProfessionUnlearnSpells(player, S_UNLEARN_AXE); + player->ModifyMoney(-DoMedUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } else + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 10: + if( EquippedOk(player,S_UNLEARN_SWORD) ) + { + if( player->GetMoney() >= DoMedUnlearnCost(player)) + { + player->CastSpell(player, S_UNLEARN_SWORD, true); + ProfessionUnlearnSpells(player, S_UNLEARN_SWORD); + player->ModifyMoney(-DoMedUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } else + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + break; + } +} + +void SendConfirmLearn_npc_prof_blacksmith(Player *player, Creature *_Creature, uint32 action) +{ + if(action) + { + uint32 eCreature = _Creature->GetEntry(); + switch(eCreature) + { + case 11191: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_HAMMER, GOSSIP_SENDER_CHECK, action); + //unknown textID (TALK_HAMMER_LEARN) + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 11192: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_AXE, GOSSIP_SENDER_CHECK, action); + //unknown textID (TALK_AXE_LEARN) + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 11193: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_SWORD, GOSSIP_SENDER_CHECK, action); + //unknown textID (TALK_SWORD_LEARN) + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + } + } +} + +void SendConfirmUnlearn_npc_prof_blacksmith(Player *player, Creature *_Creature, uint32 action) +{ + if(action) + { + uint32 eCreature = _Creature->GetEntry(); + switch(eCreature) + { + case 11146: //Ironus Coldsteel + case 11178: //Borgosh Corebender + case 5164: //Grumnus Steelshaper + case 11177: //Okothos Ironrager + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_SMITH_SPEC, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_ARMORORWEAPON, DoLowUnlearnCost(player)); + //unknown textID (TALK_UNLEARN_AXEORWEAPON) + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + + case 11191: + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_HAMMER, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_WEAPON_SPEC, DoMedUnlearnCost(player)); + //unknown textID (TALK_HAMMER_UNLEARN) + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 11192: + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_AXE, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_WEAPON_SPEC, DoMedUnlearnCost(player)); + //unknown textID (TALK_AXE_UNLEARN) + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 11193: + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_SWORD, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_WEAPON_SPEC, DoMedUnlearnCost(player)); + //unknown textID (TALK_SWORD_UNLEARN) + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + } + } +} + +bool GossipSelect_npc_prof_blacksmith(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch(sender) + { + case GOSSIP_SENDER_MAIN: SendActionMenu_npc_prof_blacksmith(player, _Creature, action); break; + case GOSSIP_SENDER_LEARN: SendConfirmLearn_npc_prof_blacksmith(player, _Creature, action); break; + case GOSSIP_SENDER_UNLEARN: SendConfirmUnlearn_npc_prof_blacksmith(player, _Creature, action); break; + case GOSSIP_SENDER_CHECK: SendActionMenu_npc_prof_blacksmith(player, _Creature, action); break; + } + return true; +} + +/*bool QuestComplete_npc_prof_blacksmith( Player *player, Creature *_Creature, Quest const *_Quest ) +{ + if ( (_Quest->GetQuestId() == 5283) || (_Quest->GetQuestId() == 5301) ) //armorsmith + _Creature->CastSpell(player, 17451, true); + + if ( (_Quest->GetQuestId() == 5284) || (_Quest->GetQuestId() == 5302) ) //weaponsmith + _Creature->CastSpell(player, 17452, true); + + return true; +}*/ + +/*### +# start menues leatherworking +###*/ + +bool GossipHello_npc_prof_leather(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + if (_Creature->isVendor()) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + if(_Creature->isTrainer()) + player->ADD_GOSSIP_ITEM(2, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN); + + uint32 eCreature = _Creature->GetEntry(); + + if(player->HasSkill(SKILL_LEATHERWORKING) && player->GetBaseSkillValue(SKILL_LEATHERWORKING)>=250 && player->getLevel() > 49 ) + { + switch (eCreature) + { + case 7866: //Peter Galen + case 7867: //Thorkaf Dragoneye + if(player->HasSpell(S_DRAGON)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_DRAGON, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 1); + break; + case 7868: //Sarah Tanner + case 7869: //Brumn Winterhoof + if(player->HasSpell(S_ELEMENTAL)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_ELEMENTAL, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 2); + break; + case 7870: //Caryssia Moonhunter + case 7871: //Se'Jib + if(player->HasSpell(S_TRIBAL)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_TRIBAL, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 3); + break; + } + } + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + return true; +} + +void SendActionMenu_npc_prof_leather(Player *player, Creature *_Creature, uint32 action) +{ + switch(action) + { + case GOSSIP_ACTION_TRADE: + player->SEND_VENDORLIST( _Creature->GetGUID() ); + break; + case GOSSIP_ACTION_TRAIN: + player->SEND_TRAINERLIST( _Creature->GetGUID() ); + break; + //Unlearn Leather + case GOSSIP_ACTION_INFO_DEF + 1: + if( EquippedOk(player,S_UNLEARN_DRAGON) ) + { + if( player->GetMoney() >= DoMedUnlearnCost(player) ) + { + player->CastSpell(player, S_UNLEARN_DRAGON, true); + ProfessionUnlearnSpells(player, S_UNLEARN_DRAGON); + player->ModifyMoney(-DoMedUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } else + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + if( EquippedOk(player,S_UNLEARN_ELEMENTAL) ) + { + if( player->GetMoney() >= DoMedUnlearnCost(player) ) + { + player->CastSpell(player, S_UNLEARN_ELEMENTAL, true); + ProfessionUnlearnSpells(player, S_UNLEARN_ELEMENTAL); + player->ModifyMoney(-DoMedUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } else + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + if( EquippedOk(player,S_UNLEARN_TRIBAL) ) + { + if(player->GetMoney() >= DoMedUnlearnCost(player)) + { + player->CastSpell(player, S_UNLEARN_TRIBAL, true); + ProfessionUnlearnSpells(player, S_UNLEARN_TRIBAL); + player->ModifyMoney(-DoMedUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } else + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + break; + } +} + +void SendConfirmUnlearn_npc_prof_leather(Player *player, Creature *_Creature, uint32 action) +{ + if(action) + { + uint32 eCreature = _Creature->GetEntry(); + switch(eCreature) + { + case 7866: //Peter Galen + case 7867: //Thorkaf Dragoneye + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_DRAGON, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_LEATHER_SPEC, DoMedUnlearnCost(player)); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 7868: //Sarah Tanner + case 7869: //Brumn Winterhoof + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_ELEMENTAL, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_LEATHER_SPEC, DoMedUnlearnCost(player)); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 7870: //Caryssia Moonhunter + case 7871: //Se'Jib + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_TRIBAL, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_LEATHER_SPEC, DoMedUnlearnCost(player)); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + } + } +} + +bool GossipSelect_npc_prof_leather(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch(sender) + { + case GOSSIP_SENDER_MAIN: SendActionMenu_npc_prof_leather(player, _Creature, action); break; + case GOSSIP_SENDER_UNLEARN: SendConfirmUnlearn_npc_prof_leather(player, _Creature, action); break; + case GOSSIP_SENDER_CHECK: SendActionMenu_npc_prof_leather(player, _Creature, action); break; + } + return true; +} + +/*### +# start menues tailoring +###*/ + +bool HasTailorSpell(Player *player) +{ + if (player->HasSpell(S_MOONCLOTH) || player->HasSpell(S_SHADOWEAVE) || player->HasSpell(S_SPELLFIRE)) + return true; + return false; +} + +bool GossipHello_npc_prof_tailor(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + if (_Creature->isVendor()) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + if (_Creature->isTrainer()) + player->ADD_GOSSIP_ITEM(2, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN); + + uint32 eCreature = _Creature->GetEntry(); + //TAILORING SPEC + if (player->HasSkill(SKILL_TAILORING) && player->GetBaseSkillValue(SKILL_TAILORING)>=350 && player->getLevel() > 59) + { + if (player->GetQuestRewardStatus(10831) || player->GetQuestRewardStatus(10832) || player->GetQuestRewardStatus(10833)) + { + switch (eCreature) + { + case 22213: //Gidge Spellweaver + if (!HasTailorSpell(player)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_SPELLFIRE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 1); + if (player->HasSpell(S_SPELLFIRE)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_SPELLFIRE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 4); + break; + case 22208: //Nasmara Moonsong + if (!HasTailorSpell(player)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_MOONCLOTH, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 2); + if (player->HasSpell(S_MOONCLOTH)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_MOONCLOTH, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 5); + break; + case 22212: //Andrion Darkspinner + if (!HasTailorSpell(player)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_SHADOWEAVE, GOSSIP_SENDER_LEARN, GOSSIP_ACTION_INFO_DEF + 3); + if (player->HasSpell(S_SHADOWEAVE)) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_UNLEARN_SHADOWEAVE, GOSSIP_SENDER_UNLEARN, GOSSIP_ACTION_INFO_DEF + 6); + break; + } + } + } + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + return true; +} + +void SendActionMenu_npc_prof_tailor(Player *player, Creature *_Creature, uint32 action) +{ + switch(action) + { + case GOSSIP_ACTION_TRADE: + player->SEND_VENDORLIST( _Creature->GetGUID() ); + break; + case GOSSIP_ACTION_TRAIN: + player->SEND_TRAINERLIST( _Creature->GetGUID() ); + break; + //Learn Tailor + case GOSSIP_ACTION_INFO_DEF + 1: + if(!player->HasSpell(S_SPELLFIRE) && player->GetMoney() >= DoLearnCost(player)) + { + player->CastSpell(player, S_LEARN_SPELLFIRE, true); + player->ModifyMoney(-DoLearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + if(!player->HasSpell(S_MOONCLOTH) && player->GetMoney() >= DoLearnCost(player)) + { + player->CastSpell(player, S_LEARN_MOONCLOTH, true); + player->ModifyMoney(-DoLearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + if(!player->HasSpell(S_SHADOWEAVE) && player->GetMoney() >= DoLearnCost(player)) + { + player->CastSpell(player, S_LEARN_SHADOWEAVE, true); + player->ModifyMoney(-DoLearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + player->CLOSE_GOSSIP_MENU(); + break; + //Unlearn Tailor + case GOSSIP_ACTION_INFO_DEF + 4: + if( EquippedOk(player,S_UNLEARN_SPELLFIRE) ) + { + if( player->GetMoney() >= DoHighUnlearnCost(player) ) + { + player->CastSpell(player, S_UNLEARN_SPELLFIRE, true); + ProfessionUnlearnSpells(player, S_UNLEARN_SPELLFIRE); + player->ModifyMoney(-DoHighUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } else + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 5: + if( EquippedOk(player,S_UNLEARN_MOONCLOTH) ) + { + if( player->GetMoney() >= DoHighUnlearnCost(player) ) + { + player->CastSpell(player, S_UNLEARN_MOONCLOTH, true); + ProfessionUnlearnSpells(player, S_UNLEARN_MOONCLOTH); + player->ModifyMoney(-DoHighUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } else + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + break; + case GOSSIP_ACTION_INFO_DEF + 6: + if( EquippedOk(player,S_UNLEARN_SHADOWEAVE) ) + { + if( player->GetMoney() >= DoHighUnlearnCost(player) ) + { + player->CastSpell(player, S_UNLEARN_SHADOWEAVE, true); + ProfessionUnlearnSpells(player, S_UNLEARN_SHADOWEAVE); + player->ModifyMoney(-DoHighUnlearnCost(player)); + } else + player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, _Creature, 0, 0); + } else + player->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,NULL,NULL); + player->CLOSE_GOSSIP_MENU(); + break; + } +} + +void SendConfirmLearn_npc_prof_tailor(Player *player, Creature *_Creature, uint32 action) +{ + if(action) + { + uint32 eCreature = _Creature->GetEntry(); + switch(eCreature) + { + case 22213: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_SPELLFIRE, GOSSIP_SENDER_CHECK, action); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 22208: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_MOONCLOTH, GOSSIP_SENDER_CHECK, action); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 22212: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_LEARN_SHADOWEAVE, GOSSIP_SENDER_CHECK, action); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + } + } +} + +void SendConfirmUnlearn_npc_prof_tailor(Player *player, Creature *_Creature, uint32 action) +{ + if(action) + { + uint32 eCreature = _Creature->GetEntry(); + switch(eCreature) + { + case 22213: //Gidge Spellweaver + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_SPELLFIRE, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_TAILOR_SPEC, DoHighUnlearnCost(player)); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 22208: //Nasmara Moonsong + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_MOONCLOTH, GOSSIP_SENDER_CHECK, action, BOX_UNLEARN_TAILOR_SPEC, DoHighUnlearnCost(player)); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case 22212: //Andrion Darkspinner + player->ADD_GOSSIP_ITEM_EXTENDED( 0, GOSSIP_UNLEARN_SHADOWEAVE, GOSSIP_SENDER_CHECK, action,BOX_UNLEARN_TAILOR_SPEC, DoHighUnlearnCost(player)); + //unknown textID () + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + } + } +} + +bool GossipSelect_npc_prof_tailor(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch(sender) + { + case GOSSIP_SENDER_MAIN: SendActionMenu_npc_prof_tailor(player, _Creature, action); break; + case GOSSIP_SENDER_LEARN: SendConfirmLearn_npc_prof_tailor(player, _Creature, action); break; + case GOSSIP_SENDER_UNLEARN: SendConfirmUnlearn_npc_prof_tailor(player, _Creature, action); break; + case GOSSIP_SENDER_CHECK: SendActionMenu_npc_prof_tailor(player, _Creature, action); break; + } + return true; +} + +/*### +# start menues for GO (engineering and leatherworking) +###*/ + +/*bool GOHello_go_soothsaying_for_dummies(Player *player, GameObject* _GO) +{ + player->PlayerTalkClass->GetGossipMenu()->AddMenuItem(0,GOSSIP_LEARN_DRAGON, GOSSIP_SENDER_INFO, GOSSIP_ACTION_INFO_DEF, "", 0); + + player->SEND_GOSSIP_MENU(5584, _GO->GetGUID()); + + return true; +}*/ + +/*### +# +###*/ + +void AddSC_npc_professions() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_prof_alchemy"; + newscript->pGossipHello = &GossipHello_npc_prof_alchemy; + newscript->pGossipSelect = &GossipSelect_npc_prof_alchemy; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_prof_blacksmith"; + newscript->pGossipHello = &GossipHello_npc_prof_blacksmith; + newscript->pGossipSelect = &GossipSelect_npc_prof_blacksmith; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_prof_leather"; + newscript->pGossipHello = &GossipHello_npc_prof_leather; + newscript->pGossipSelect = &GossipSelect_npc_prof_leather; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_prof_tailor"; + newscript->pGossipHello = &GossipHello_npc_prof_tailor; + newscript->pGossipSelect = &GossipSelect_npc_prof_tailor; + m_scripts[nrscripts++] = newscript; + + /*newscript = new Script; + newscript->Name="go_soothsaying_for_dummies"; + newscript->pGOHello = &GOHello_go_soothsaying_for_dummies; + //newscript->pGossipSelect = &GossipSelect_go_soothsaying_for_dummies; + m_scripts[nrscripts++] = newscript;*/ +} diff --git a/src/bindings/scripts/scripts/npc/npcs_special.cpp b/src/bindings/scripts/scripts/npc/npcs_special.cpp new file mode 100644 index 00000000000..a8ea8612c58 --- /dev/null +++ b/src/bindings/scripts/scripts/npc/npcs_special.cpp @@ -0,0 +1,878 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Npcs_Special +SD%Complete: 100 +SDComment: To be used for special NPCs that are located globally. Support for quest 3861 (Cluck!), 6622 and 6624 (Triage) +SDCategory: NPCs +EndScriptData +*/ + +/* ContentData +npc_chicken_cluck 100% support for quest 3861 (Cluck!) +npc_dancing_flames 100% midsummer event NPC +npc_guardian 100% guardianAI used to prevent players from accessing off-limits areas. Not in use by SD2 +npc_injured_patient 100% patients for triage-quests (6622 and 6624) +npc_doctor 100% Gustaf Vanhowzen and Gregory Victor, quest 6622 and 6624 (Triage) +npc_mount_vendor 100% Regular mount vendors all over the world. Display gossip if player doesn't meet the requirements to buy +npc_rogue_trainer 80% Scripted trainers, so they are able to offer item 17126 for class quest 6681 +npc_sayge 100% Darkmoon event fortune teller, buff player based on answers given +EndContentData */ + +#include "precompiled.h" + +/*######## +# npc_chicken_cluck +#########*/ + +#define QUEST_CLUCK 3861 +#define EMOTE_A_HELLO "looks up at you quizzically. Maybe you should inspect it?" +#define EMOTE_H_HELLO "looks at you unexpectadly." +#define CLUCK_TEXT2 "starts pecking at the feed." +#define FACTION_FRIENDLY 84 +#define FACTION_CHICKEN 31 + +struct MANGOS_DLL_DECL npc_chicken_cluckAI : public ScriptedAI +{ + npc_chicken_cluckAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ResetFlagTimer; + + void Reset() + { + ResetFlagTimer = 120000; + + m_creature->setFaction(FACTION_CHICKEN); + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + // Reset flags after a certain time has passed so that the next player has to start the 'event' again + if(m_creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER)) + { + if(ResetFlagTimer < diff) + EnterEvadeMode(); + else ResetFlagTimer -= diff; + } + + if(m_creature->SelectHostilTarget() && m_creature->getVictim()) + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_chicken_cluck(Creature *_Creature) +{ + return new npc_chicken_cluckAI(_Creature); +} + +bool ReceiveEmote_npc_chicken_cluck( Player *player, Creature *_Creature, uint32 emote ) +{ + if( emote == TEXTEMOTE_CHICKEN ) + { + if( player->GetTeam() == ALLIANCE ) + { + if( rand()%30 == 1 ) + { + if( player->GetQuestStatus(QUEST_CLUCK) == QUEST_STATUS_NONE ) + { + _Creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + _Creature->setFaction(FACTION_FRIENDLY); + _Creature->MonsterTextEmote(EMOTE_A_HELLO, 0); + } + } + } else + _Creature->MonsterTextEmote(EMOTE_H_HELLO,0); + } + if( emote == TEXTEMOTE_CHEER && player->GetTeam() == ALLIANCE ) + if( player->GetQuestStatus(QUEST_CLUCK) == QUEST_STATUS_COMPLETE ) + { + _Creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + _Creature->setFaction(FACTION_FRIENDLY); + _Creature->MonsterTextEmote(CLUCK_TEXT2, 0); + } + + return true; +} + +bool QuestAccept_npc_chicken_cluck(Player *player, Creature *_Creature, const Quest *_Quest ) +{ + if(_Quest->GetQuestId() == QUEST_CLUCK) + ((npc_chicken_cluckAI*)_Creature->AI())->Reset(); + + return true; +} + +bool QuestComplete_npc_chicken_cluck(Player *player, Creature *_Creature, const Quest *_Quest) +{ + if(_Quest->GetQuestId() == QUEST_CLUCK) + ((npc_chicken_cluckAI*)_Creature->AI())->Reset(); + + return true; +} + +/*###### +## npc_dancing_flames +######*/ + +bool ReceiveEmote_npc_dancing_flames( Player *player, Creature *_Creature, uint32 emote ) +{ + if( emote == TEXTEMOTE_DANCE ) + _Creature->CastSpell(player,47057,false); + + return true; +} + +/*###### +## Triage quest +######*/ + +#define SAY_DOC1 "I'm saved! Thank you, doctor!" +#define SAY_DOC2 "HOORAY! I AM SAVED!" +#define SAY_DOC3 "Sweet, sweet embrace... take me..." + +struct Location +{ + float x, y, z, o; +}; + +#define DOCTOR_ALLIANCE 12939 + +static Location AllianceCoords[]= +{ + { // Top-far-right bunk as seen from entrance + -3757.38, -4533.05, 14.16, 3.62 + }, + { // Top-far-left bunk + -3754.36, -4539.13, 14.16, 5.13 + }, + { // Far-right bunk + -3749.54, -4540.25, 14.28, 3.34 + }, + { // Right bunk near entrance + -3742.10, -4536.85, 14.28, 3.64 + }, + { // Far-left bunk + -3755.89, -4529.07, 14.05, 0.57 + }, + { // Mid-left bunk + -3749.51, -4527.08, 14.07, 5.26 + }, + { // Left bunk near entrance + -3746.37, -4525.35, 14.16, 5.22 + }, +}; + +#define ALLIANCE_COORDS 7 + +//alliance run to where +#define A_RUNTOX -3742.96 +#define A_RUNTOY -4531.52 +#define A_RUNTOZ 11.91 + +#define DOCTOR_HORDE 12920 + +static Location HordeCoords[]= +{ + { // Left, Behind + -1013.75, -3492.59, 62.62, 4.34 + }, + { // Right, Behind + -1017.72, -3490.92, 62.62, 4.34 + }, + { // Left, Mid + -1015.77, -3497.15, 62.82, 4.34 + }, + { // Right, Mid + -1019.51, -3495.49, 62.82, 4.34 + }, + { // Left, front + -1017.25, -3500.85, 62.98, 4.34 + }, + { // Right, Front + -1020.95, -3499.21, 62.98, 4.34 + } +}; + +#define HORDE_COORDS 6 + +//horde run to where +#define H_RUNTOX -1016.44 +#define H_RUNTOY -3508.48 +#define H_RUNTOZ 62.96 + +const uint32 AllianceSoldierId[3] = +{ + 12938, // 12938 Injured Alliance Soldier + 12936, // 12936 Badly injured Alliance Soldier + 12937 // 12937 Critically injured Alliance Soldier +}; + +const uint32 HordeSoldierId[3] = +{ + 12923, //12923 Injured Soldier + 12924, //12924 Badly injured Soldier + 12925 //12925 Critically injured Soldier +}; + +/*###### +## npc_doctor (handles both Gustaf Vanhowzen and Gregory Victor) +######*/ + +struct MANGOS_DLL_DECL npc_doctorAI : public ScriptedAI +{ + uint64 Playerguid; + + uint32 SummonPatient_Timer; + uint32 SummonPatientCount; + uint32 PatientDiedCount; + uint32 PatientSavedCount; + + bool Event; + + std::list Patients; + std::vector Coordinates; + + npc_doctorAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset(){} + + void BeginEvent(Player* player); + void PatientDied(Location* Point); + void PatientSaved(Creature* soldier, Player* player, Location* Point); + void UpdateAI(const uint32 diff); + + void Aggro(Unit* who){} +}; + +/*##### +## npc_injured_patient (handles all the patients, no matter Horde or Alliance) +#####*/ + +struct MANGOS_DLL_DECL npc_injured_patientAI : public ScriptedAI +{ + npc_injured_patientAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint64 Doctorguid; + + Location* Coord; + + void Reset() + { + Doctorguid = 0; + + Coord = NULL; + //no select + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + //no regen health + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + //to make them lay with face down + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, PLAYER_STATE_DEAD); + + uint32 mobId = m_creature->GetEntry(); + + switch (mobId) + { //lower max health + case 12923: + case 12938: //Injured Soldier + m_creature->SetHealth(uint32(m_creature->GetMaxHealth()*.75)); + break; + case 12924: + case 12936: //Badly injured Soldier + m_creature->SetHealth(uint32(m_creature->GetMaxHealth()*.50)); + break; + case 12925: + case 12937: //Critically injured Soldier + m_creature->SetHealth(uint32(m_creature->GetMaxHealth()*.25)); + break; + } + } + + void Aggro(Unit* who){} + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + if (caster->GetTypeId() == TYPEID_PLAYER && m_creature->isAlive() && spell->Id == 20804) + { + if( (((Player*)caster)->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE) || (((Player*)caster)->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE)) + { + if(Doctorguid) + { + Creature* Doctor = ((Creature*)Unit::GetUnit((*m_creature), Doctorguid)); + if(Doctor) + ((npc_doctorAI*)Doctor->AI())->PatientSaved(m_creature, ((Player*)caster), Coord); + } + } + //make not selectable + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + //regen health + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + //stand up + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, PLAYER_STATE_NONE); + DoSay(SAY_DOC1,LANG_UNIVERSAL,NULL); + + uint32 mobId = m_creature->GetEntry(); + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + switch (mobId) + { + case 12923: + case 12924: + case 12925: + m_creature->GetMotionMaster()->MovePoint(0, H_RUNTOX, H_RUNTOY, H_RUNTOZ); + break; + case 12936: + case 12937: + case 12938: + m_creature->GetMotionMaster()->MovePoint(0, A_RUNTOX, A_RUNTOY, A_RUNTOZ); + break; + } + } + return; + } + + void UpdateAI(const uint32 diff) + { + if (m_creature->isAlive() && m_creature->GetHealth() > 6) + { //lower HP on every world tick makes it a useful counter, not officlone though + m_creature->SetHealth(uint32(m_creature->GetHealth()-5) ); + } + + if (m_creature->isAlive() && m_creature->GetHealth() <= 6) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->setDeathState(JUST_DIED); + m_creature->SetFlag(UNIT_DYNAMIC_FLAGS, 32); + + if(Doctorguid) + { + Creature* Doctor = ((Creature*)Unit::GetUnit((*m_creature), Doctorguid)); + if(Doctor) + ((npc_doctorAI*)Doctor->AI())->PatientDied(Coord); + } + } + } +}; + +CreatureAI* GetAI_npc_injured_patient(Creature *_Creature) +{ + return new npc_injured_patientAI (_Creature); +} + +/* +npc_doctor (continue) +*/ + +void npc_doctorAI::BeginEvent(Player* player) +{ + Playerguid = player->GetGUID(); + + SummonPatient_Timer = 10000; + SummonPatientCount = 0; + PatientDiedCount = 0; + PatientSavedCount = 0; + + switch(m_creature->GetEntry()) + { + case DOCTOR_ALLIANCE: + for(uint8 i = 0; i < ALLIANCE_COORDS; ++i) + Coordinates.push_back(&AllianceCoords[i]); + break; + + case DOCTOR_HORDE: + for(uint8 i = 0; i < HORDE_COORDS; ++i) + Coordinates.push_back(&HordeCoords[i]); + break; + } + + Event = true; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); +} + +void npc_doctorAI::PatientDied(Location* Point) +{ + Player* player = ((Player*)Unit::GetUnit((*m_creature), Playerguid)); + if(player && ((player->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE) || (player->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE))) + { + PatientDiedCount++; + if (PatientDiedCount > 5 && Event) + { + if(player->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE) + player->FailQuest(6624); + else if(player->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE) + player->FailQuest(6622); + + Event = false; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + Coordinates.push_back(Point); + } +} + +void npc_doctorAI::PatientSaved(Creature* soldier, Player* player, Location* Point) +{ + if(player && Playerguid == player->GetGUID()) + { + if((player->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE) || (player->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE)) + { + PatientSavedCount++; + if(PatientSavedCount == 15) + { + if(!Patients.empty()) + { + std::list::iterator itr; + for(itr = Patients.begin(); itr != Patients.end(); ++itr) + { + Creature* Patient = ((Creature*)Unit::GetUnit((*m_creature), *itr)); + if( Patient ) + Patient->setDeathState(JUST_DIED); + } + } + + if(player->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE) + player->AreaExploredOrEventHappens(6624); + else if(player->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE) + player->AreaExploredOrEventHappens(6622); + + Event = false; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + Coordinates.push_back(Point); + } + } +} + +void npc_doctorAI::UpdateAI(const uint32 diff) +{ + if(Event && SummonPatientCount >= 20) + { + Event = false; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + if(Event) + if(SummonPatient_Timer < diff) + { + Creature* Patient = NULL; + Location* Point = NULL; + + if(Coordinates.empty()) + return; + + std::vector::iterator itr = Coordinates.begin()+rand()%Coordinates.size(); + uint32 patientEntry = 0; + + switch(m_creature->GetEntry()) + { + case DOCTOR_ALLIANCE: patientEntry = AllianceSoldierId[rand()%3]; break; + case DOCTOR_HORDE: patientEntry = HordeSoldierId[rand()%3]; break; + default: + error_log("SD2: Invalid entry for Triage doctor. Please check your database"); + return; + } + + Point = *itr; + + Patient = m_creature->SummonCreature(patientEntry, Point->x, Point->y, Point->z, Point->o, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + + if(Patient) + { + Patients.push_back(Patient->GetGUID()); + ((npc_injured_patientAI*)Patient->AI())->Doctorguid = m_creature->GetGUID(); + if(Point) + ((npc_injured_patientAI*)Patient->AI())->Coord = Point; + Coordinates.erase(itr); + } + SummonPatient_Timer = 10000; + SummonPatientCount++; + }else SummonPatient_Timer -= diff; +} + +bool QuestAccept_npc_doctor(Player *player, Creature *creature, Quest const *quest ) +{ + if((quest->GetQuestId() == 6624) || (quest->GetQuestId() == 6622)) + ((npc_doctorAI*)creature->AI())->BeginEvent(player); + + return true; +} + +CreatureAI* GetAI_npc_doctor(Creature *_Creature) +{ + return new npc_doctorAI (_Creature); +} + +/*###### +## npc_guardian +######*/ + +#define SPELL_DEATHTOUCH 5 +#define SAY_AGGRO "This area is closed!" + +struct MANGOS_DLL_DECL npc_guardianAI : public ScriptedAI +{ + npc_guardianAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (m_creature->isAttackReady()) + { + m_creature->CastSpell(m_creature->getVictim(),SPELL_DEATHTOUCH, true); + m_creature->resetAttackTimer(); + } + } +}; + +CreatureAI* GetAI_npc_guardian(Creature *_Creature) +{ + return new npc_guardianAI (_Creature); +} + +/*###### +## npc_mount_vendor +######*/ + +bool GossipHello_npc_mount_vendor(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + bool canBuy; + canBuy = false; + uint32 vendor = _Creature->GetEntry(); + uint8 race = player->getRace(); + + switch (vendor) + { + case 384: //Katie Hunter + case 1460: //Unger Statforth + case 2357: //Merideth Carlson + case 4885: //Gregor MacVince + if (player->GetReputationRank(72) != REP_EXALTED && race != RACE_HUMAN) + player->SEND_GOSSIP_MENU(5855, _Creature->GetGUID()); + else canBuy = true; + break; + case 1261: //Veron Amberstill + if (player->GetReputationRank(47) != REP_EXALTED && race != RACE_DWARF) + player->SEND_GOSSIP_MENU(5856, _Creature->GetGUID()); + else canBuy = true; + break; + case 3362: //Ogunaro Wolfrunner + if (player->GetReputationRank(76) != REP_EXALTED && race != RACE_ORC) + player->SEND_GOSSIP_MENU(5841, _Creature->GetGUID()); + else canBuy = true; + break; + case 3685: //Harb Clawhoof + if (player->GetReputationRank(81) != REP_EXALTED && race != RACE_TAUREN) + player->SEND_GOSSIP_MENU(5843, _Creature->GetGUID()); + else canBuy = true; + break; + case 4730: //Lelanai + if (player->GetReputationRank(69) != REP_EXALTED && race != RACE_NIGHTELF) + player->SEND_GOSSIP_MENU(5844, _Creature->GetGUID()); + else canBuy = true; + break; + case 4731: //Zachariah Post + if (player->GetReputationRank(68) != REP_EXALTED && race != RACE_UNDEAD_PLAYER) + player->SEND_GOSSIP_MENU(5840, _Creature->GetGUID()); + else canBuy = true; + break; + case 7952: //Zjolnir + if (player->GetReputationRank(530) != REP_EXALTED && race != RACE_TROLL) + player->SEND_GOSSIP_MENU(5842, _Creature->GetGUID()); + else canBuy = true; + break; + case 7955: //Milli Featherwhistle + if (player->GetReputationRank(54) != REP_EXALTED && race != RACE_GNOME) + player->SEND_GOSSIP_MENU(5857, _Creature->GetGUID()); + else canBuy = true; + break; + case 16264: //Winaestra + if (player->GetReputationRank(911) != REP_EXALTED && race != RACE_BLOODELF) + player->SEND_GOSSIP_MENU(10305, _Creature->GetGUID()); + else canBuy = true; + break; + case 17584: //Torallius the Pack Handler + if (player->GetReputationRank(930) != REP_EXALTED && race != RACE_DRAENEI) + player->SEND_GOSSIP_MENU(10239, _Creature->GetGUID()); + else canBuy = true; + break; + } + + if (canBuy) + { + if (_Creature->isVendor()) + player->ADD_GOSSIP_ITEM( 1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + } + return true; +} + +bool GossipSelect_npc_mount_vendor(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_TRADE) + player->SEND_VENDORLIST( _Creature->GetGUID() ); + + return true; +} + +/*###### +## npc_rogue_trainer +######*/ + +bool GossipHello_npc_rogue_trainer(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( _Creature->isTrainer() ) + player->ADD_GOSSIP_ITEM(2, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN); + + if( _Creature->isCanTrainingAndResetTalentsOf(player) ) + player->ADD_GOSSIP_ITEM(2, "I wish to unlearn my talents", GOSSIP_SENDER_MAIN, GOSSIP_OPTION_UNLEARNTALENTS); + + if( player->getClass() == CLASS_ROGUE && player->getLevel() >= 24 && !player->HasItemCount(17126,1) && !player->GetQuestRewardStatus(6681) ) + { + player->ADD_GOSSIP_ITEM(0, "", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(5996, _Creature->GetGUID()); + } else + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_rogue_trainer(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch( action ) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,21100,false); + break; + case GOSSIP_ACTION_TRAIN: + player->SEND_TRAINERLIST( _Creature->GetGUID() ); + break; + case GOSSIP_OPTION_UNLEARNTALENTS: + player->CLOSE_GOSSIP_MENU(); + player->SendTalentWipeConfirm( _Creature->GetGUID() ); + break; + } + return true; +} + +/*###### +## npc_sayge +######*/ + +#define SPELL_DMG 23768 //dmg +#define SPELL_RES 23769 //res +#define SPELL_ARM 23767 //arm +#define SPELL_SPI 23738 //spi +#define SPELL_INT 23766 //int +#define SPELL_STM 23737 //stm +#define SPELL_STR 23735 //str +#define SPELL_AGI 23736 //agi +#define SPELL_FORTUNE 23765 //faire fortune + +bool GossipHello_npc_sayge(Player *player, Creature *_Creature) +{ + if(_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( player->HasSpellCooldown(SPELL_INT) || + player->HasSpellCooldown(SPELL_ARM) || + player->HasSpellCooldown(SPELL_DMG) || + player->HasSpellCooldown(SPELL_RES) || + player->HasSpellCooldown(SPELL_STR) || + player->HasSpellCooldown(SPELL_AGI) || + player->HasSpellCooldown(SPELL_STM) || + player->HasSpellCooldown(SPELL_SPI) ) + player->SEND_GOSSIP_MENU(7393, _Creature->GetGUID()); + else + { + player->ADD_GOSSIP_ITEM(0, "Yes", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(7339, _Creature->GetGUID()); + } + + return true; +} + +void SendAction_npc_sayge(Player *player, Creature *_Creature, uint32 action) +{ + switch(action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM(0, "Slay the Man", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->ADD_GOSSIP_ITEM(0, "Turn him over to liege", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->ADD_GOSSIP_ITEM(0, "Confiscate the corn", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + player->ADD_GOSSIP_ITEM(0, "Let him go and have the corn", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(7340, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM(0, "Execute your friend painfully", GOSSIP_SENDER_MAIN+1, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM(0, "Execute your friend painlessly", GOSSIP_SENDER_MAIN+2, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM(0, "Let your friend go", GOSSIP_SENDER_MAIN+3, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(7341, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM(0, "Confront the diplomat", GOSSIP_SENDER_MAIN+4, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM(0, "Show not so quiet defiance", GOSSIP_SENDER_MAIN+5, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM(0, "Remain quiet", GOSSIP_SENDER_MAIN+2, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(7361, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM(0, "Speak against your brother openly", GOSSIP_SENDER_MAIN+6, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM(0, "Help your brother in", GOSSIP_SENDER_MAIN+7, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM(0, "Keep your brother out without letting him know", GOSSIP_SENDER_MAIN+8, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(7362, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->ADD_GOSSIP_ITEM(0, "Take credit, keep gold", GOSSIP_SENDER_MAIN+5, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM(0, "Take credit, share the gold", GOSSIP_SENDER_MAIN+4, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM(0, "Let the knight take credit", GOSSIP_SENDER_MAIN+3, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(7363, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM(0, "Thanks", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+6); + player->SEND_GOSSIP_MENU(7364, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + _Creature->CastSpell(player, SPELL_FORTUNE, false); + player->SEND_GOSSIP_MENU(7365, _Creature->GetGUID()); + break; + } +} + +bool GossipSelect_npc_sayge(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch(sender) + { + case GOSSIP_SENDER_MAIN: + SendAction_npc_sayge(player, _Creature, action); + break; + case GOSSIP_SENDER_MAIN+1: + _Creature->CastSpell(player, SPELL_DMG, false); + player->AddSpellCooldown(SPELL_DMG,0,time(NULL) + 7200); + SendAction_npc_sayge(player, _Creature, action); + break; + case GOSSIP_SENDER_MAIN+2: + _Creature->CastSpell(player, SPELL_RES, false); + player->AddSpellCooldown(SPELL_RES,0,time(NULL) + 7200); + SendAction_npc_sayge(player, _Creature, action); + break; + case GOSSIP_SENDER_MAIN+3: + _Creature->CastSpell(player, SPELL_ARM, false); + player->AddSpellCooldown(SPELL_ARM,0,time(NULL) + 7200); + SendAction_npc_sayge(player, _Creature, action); + break; + case GOSSIP_SENDER_MAIN+4: + _Creature->CastSpell(player, SPELL_SPI, false); + player->AddSpellCooldown(SPELL_SPI,0,time(NULL) + 7200); + SendAction_npc_sayge(player, _Creature, action); + break; + case GOSSIP_SENDER_MAIN+5: + _Creature->CastSpell(player, SPELL_INT, false); + player->AddSpellCooldown(SPELL_INT,0,time(NULL) + 7200); + SendAction_npc_sayge(player, _Creature, action); + break; + case GOSSIP_SENDER_MAIN+6: + _Creature->CastSpell(player, SPELL_STM, false); + player->AddSpellCooldown(SPELL_STM,0,time(NULL) + 7200); + SendAction_npc_sayge(player, _Creature, action); + break; + case GOSSIP_SENDER_MAIN+7: + _Creature->CastSpell(player, SPELL_STR, false); + player->AddSpellCooldown(SPELL_STR,0,time(NULL) + 7200); + SendAction_npc_sayge(player, _Creature, action); + break; + case GOSSIP_SENDER_MAIN+8: + _Creature->CastSpell(player, SPELL_AGI, false); + player->AddSpellCooldown(SPELL_AGI,0,time(NULL) + 7200); + SendAction_npc_sayge(player, _Creature, action); + break; + } + return true; +} + +void AddSC_npcs_special() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_chicken_cluck"; + newscript->GetAI = GetAI_npc_chicken_cluck; + newscript->pReceiveEmote = &ReceiveEmote_npc_chicken_cluck; + newscript->pQuestAccept = &QuestAccept_npc_chicken_cluck; + newscript->pQuestComplete = &QuestComplete_npc_chicken_cluck; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_dancing_flames"; + newscript->pReceiveEmote = &ReceiveEmote_npc_dancing_flames; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_injured_patient"; + newscript->GetAI = GetAI_npc_injured_patient; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_doctor"; + newscript->GetAI = GetAI_npc_doctor; + newscript->pQuestAccept = &QuestAccept_npc_doctor; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_guardian"; + newscript->GetAI = GetAI_npc_guardian; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_mount_vendor"; + newscript->pGossipHello = &GossipHello_npc_mount_vendor; + newscript->pGossipSelect = &GossipSelect_npc_mount_vendor; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_rogue_trainer"; + newscript->pGossipHello = &GossipHello_npc_rogue_trainer; + newscript->pGossipSelect = &GossipSelect_npc_rogue_trainer; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_sayge"; + newscript->pGossipHello = &GossipHello_npc_sayge; + newscript->pGossipSelect = &GossipSelect_npc_sayge; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/alterac_mountains/alterac_mountains.cpp b/src/bindings/scripts/scripts/zone/alterac_mountains/alterac_mountains.cpp new file mode 100644 index 00000000000..074bf2967b2 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/alterac_mountains/alterac_mountains.cpp @@ -0,0 +1,62 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Alterac_Mountains +SD%Complete: 100 +SDComment: Quest support: 6681 +SDCategory: Alterac Mountains +EndScriptData */ + +/* ContentData +npc_ravenholdt +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_ravenholdt +######*/ + +struct MANGOS_DLL_DECL npc_ravenholdtAI : public ScriptedAI +{ + npc_ravenholdtAI(Creature *c) : ScriptedAI(c) { Reset(); } + + void Reset() { } + + void MoveInLineOfSight(Unit *who) + { + if( who->GetTypeId() == TYPEID_PLAYER ) + if( ((Player*)who)->GetQuestStatus(6681) == QUEST_STATUS_INCOMPLETE ) + ((Player*)who)->KilledMonster(m_creature->GetEntry(),m_creature->GetGUID() ); + } + + void Aggro(Unit* who) { } +}; +CreatureAI* GetAI_npc_ravenholdt(Creature *_Creature) +{ + return new npc_ravenholdtAI (_Creature); +} + +void AddSC_alterac_mountains() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_ravenholdt"; + newscript->GetAI = GetAI_npc_ravenholdt; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/auchenai_crypts/boss_exarch_maladaar.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/auchenai_crypts/boss_exarch_maladaar.cpp new file mode 100644 index 00000000000..77b0d7eaedd --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/auchenai_crypts/boss_exarch_maladaar.cpp @@ -0,0 +1,405 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Exarch_Maladaar +SD%Complete: 95 +SDComment: Most of event implemented, some adjustments to timers remain and possibly make some better code for switching his dark side in to better "images" of player. +SDCategory: Auchindoun, Auchenai Crypts +EndScriptData */ + +/* ContentData +mob_stolen_soul +boss_exarch_maladaar +mob_avatar_of_martyred +EndContentData */ + +#include "precompiled.h" + +#define SPELL_MOONFIRE 37328 +#define SPELL_FIREBALL 37329 +#define SPELL_MIND_FLAY 37330 +#define SPELL_HEMORRHAGE 37331 +#define SPELL_FROSTSHOCK 37332 +#define SPELL_CURSE_OF_AGONY 37334 +#define SPELL_MORTAL_STRIKE 37335 +#define SPELL_FREEZING_TRAP 37368 +#define SPELL_HAMMER_OF_JUSTICE 37369 + +struct MANGOS_DLL_DECL mob_stolen_soulAI : public ScriptedAI +{ + mob_stolen_soulAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint8 myClass; + uint32 Class_Timer; + + void Reset() + { + Class_Timer = 1000; + } + + void Aggro(Unit *who) + { } + + void SetMyClass(uint8 myclass) + { + myClass = myclass; + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( Class_Timer < diff ) + { + switch( myClass ) + { + case CLASS_WARRIOR: + DoCast(m_creature->getVictim(), SPELL_MORTAL_STRIKE); + Class_Timer = 6000; + break; + case CLASS_PALADIN: + DoCast(m_creature->getVictim(), SPELL_HAMMER_OF_JUSTICE); + Class_Timer = 6000; + break; + case CLASS_HUNTER: + DoCast(m_creature->getVictim(), SPELL_FREEZING_TRAP); + Class_Timer = 20000; + break; + case CLASS_ROGUE: + DoCast(m_creature->getVictim(), SPELL_HEMORRHAGE); + Class_Timer = 10000; + break; + case CLASS_PRIEST: + DoCast(m_creature->getVictim(), SPELL_MIND_FLAY); + Class_Timer = 5000; + break; + case CLASS_SHAMAN: + DoCast(m_creature->getVictim(), SPELL_FROSTSHOCK); + Class_Timer = 8000; + break; + case CLASS_MAGE: + DoCast(m_creature->getVictim(), SPELL_FIREBALL); + Class_Timer = 5000; + break; + case CLASS_WARLOCK: + DoCast(m_creature->getVictim(), SPELL_CURSE_OF_AGONY); + Class_Timer = 20000; + break; + case CLASS_DRUID: + DoCast(m_creature->getVictim(), SPELL_MOONFIRE); + Class_Timer = 10000; + break; + default: + break; + } + }else Class_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_stolen_soul(Creature *_Creature) +{ + return new mob_stolen_soulAI (_Creature); +} + +#define SAY_INTRO "You have defiled the resting place of our ancestors. For this offense, there can be but one punishment. It is fitting that you have come to a place of the dead... for you will soon be joining them." +#define SOUND_INTRO 10509 +#define SAY_SUMMON "Rise my fallen brothers. Take form and fight!" +#define SOUND_SUMMON 10512 + +#define SAY_AGGRO_1 "You will pay with your life!" +#define SOUND_AGGRO_1 10513 +#define SAY_AGGRO_2 "There's no turning back now!" +#define SOUND_AGGRO_2 10514 +#define SAY_AGGRO_3 "Serve your penitence!" +#define SOUND_AGGRO_3 10515 + +#define SAY_ROAR "Let your mind be clouded." +#define SOUND_ROAR 10510 +#define SAY_SOUL_CLEAVE "Stare into the darkness of your soul." +#define SOUND_SOUL_CLEAVE 10511 + +#define SAY_SLAY_1 "These walls will be your doom." +#define SOUND_SLAY_1 10516 +#define SAY_SLAY_2 " Now, you'll stay for eternity!" +#define SOUND_SLAY_2 10517 + +#define SAY_DEATH "This is... where.. I belong..." +#define SOUND_DEATH 10518 + +#define SPELL_RIBBON_OF_SOULS 32422 +#define SPELL_SOUL_SCREAM 32421 + +#define SPELL_STOLEN_SOUL 32346 +#define SPELL_STOLEN_SOUL_VISUAL 32395 + +#define SPELL_SUMMON_AVATAR 32424 + +#define ENTRY_STOLEN_SOUL 18441 + +struct MANGOS_DLL_DECL boss_exarch_maladaarAI : public ScriptedAI +{ + boss_exarch_maladaarAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 soulmodel; + uint64 soulholder; + uint8 soulclass; + + uint32 Fear_timer; + uint32 Ribbon_of_Souls_timer; + uint32 StolenSoul_Timer; + + bool HasTaunted; + bool Avatar_summoned; + + void Reset() + { + soulmodel = 0; + soulholder = 0; + soulclass = 0; + + Fear_timer = 20000; + Ribbon_of_Souls_timer = 5000; + StolenSoul_Timer = 30000; + + HasTaunted = false; + Avatar_summoned = false; + } + + void MoveInLineOfSight(Unit *who) + { + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if( !HasTaunted && m_creature->IsWithinDistInMap(who, 150.0) ) + { + DoYell(SAY_INTRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_INTRO); + HasTaunted = true; + } + + if (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) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + break; + case 1: + DoYell(SAY_AGGRO_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + break; + case 2: + DoYell(SAY_AGGRO_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + break; + } + } + + void JustSummoned(Creature *summoned) + { + if( summoned->GetEntry() == ENTRY_STOLEN_SOUL ) + { + //SPELL_STOLEN_SOUL_VISUAL has shapeshift effect, but not implemented feature in mangos for this spell. + summoned->SetDisplayId(soulmodel); + summoned->CastSpell(summoned,SPELL_STOLEN_SOUL_VISUAL,false); + + if( Unit *target = Unit::GetUnit(*m_creature,soulholder) ) + summoned->AI()->AttackStart(target); + + ((mob_stolen_soulAI*)summoned->AI())->SetMyClass(soulclass); + } + } + + void KilledUnit(Unit* victim) + { + if (rand()%2) + return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_1); + break; + case 1: + DoYell(SAY_SLAY_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + + //When Exarch Maladar is defeated D'ore appear. + DoSpawnCreature(19412,0,0,0,0, TEMPSUMMON_TIMED_DESPAWN, 600000); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( !Avatar_summoned && ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 25) ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(true); + + DoYell(SAY_SUMMON, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SUMMON); + + DoCast(m_creature, SPELL_SUMMON_AVATAR); + Avatar_summoned = true; + StolenSoul_Timer = 45000; + } + + if( StolenSoul_Timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + { + if( target->GetTypeId() == TYPEID_PLAYER ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(true); + + uint32 i = urand(1,2); + if( i == 1 ) + { + DoYell(SAY_ROAR, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_ROAR); + } + else + { + DoYell(SAY_SOUL_CLEAVE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SOUL_CLEAVE); + } + + soulmodel = target->GetDisplayId(); + soulholder = target->GetGUID(); + soulclass = target->getClass(); + + DoCast(target,SPELL_STOLEN_SOUL); + DoSpawnCreature(ENTRY_STOLEN_SOUL,0,0,0,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + + StolenSoul_Timer = 45000; + } else StolenSoul_Timer = 1000; + } + }else StolenSoul_Timer -= diff; + + if( Ribbon_of_Souls_timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_RIBBON_OF_SOULS); + + Ribbon_of_Souls_timer = 5000 + (rand()%20 * 1000); + }else Ribbon_of_Souls_timer -= diff; + + if( Fear_timer < diff ) + { + DoCast(m_creature,SPELL_SOUL_SCREAM); + Fear_timer = 25000 + rand()% 10000; + }else Fear_timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_exarch_maladaar(Creature *_Creature) +{ + return new boss_exarch_maladaarAI (_Creature); +} + +#define SPELL_MORTAL_STRIKE 16856 +#define SPELL_SUNDER_ARMOR 16145 + +struct MANGOS_DLL_DECL mob_avatar_of_martyredAI : public ScriptedAI +{ + mob_avatar_of_martyredAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Mortal_Strike_timer; + + void Reset() + { + Mortal_Strike_timer = 10000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(Mortal_Strike_timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_MORTAL_STRIKE); + Mortal_Strike_timer = 10000 + rand()%20 * 1000; + }else Mortal_Strike_timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_avatar_of_martyred(Creature *_Creature) +{ + return new mob_avatar_of_martyredAI (_Creature); +} + +void AddSC_boss_exarch_maladaar() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_exarch_maladaar"; + newscript->GetAI = GetAI_boss_exarch_maladaar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_avatar_of_martyred"; + newscript->GetAI = GetAI_mob_avatar_of_martyred; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_stolen_soul"; + newscript->GetAI = GetAI_mob_stolen_soul; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_nexusprince_shaffar.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_nexusprince_shaffar.cpp new file mode 100644 index 00000000000..7191ea7a663 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_nexusprince_shaffar.cpp @@ -0,0 +1,313 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_NexusPrince_Shaffar +SD%Complete: 80 +SDComment: Need more tuning of spell timers, it should not be as linear fight as current. Also should possibly find a better way to deal with his three initial beacons to make sure all aggro. +SDCategory: Auchindoun, Mana Tombs +EndScriptData */ + +/* ContentData +boss_nexusprince_shaffar +mob_ethereal_beacon +EndContentData */ + +#include "precompiled.h" + +#define SAY_INTRO "What is this? You must forgive me, but I was not expecting company. As you can see, we are somewhat preoccupied right now. But no matter. As I am a gracious host, I will tend to you... personally." +#define SOUND_INTRO 10539 + +#define SAY_AGGRO_1 "We have not yet been properly introduced." +#define SOUND_AGGRO_1 10541 +#define SAY_AGGRO_2 "An epic battle. How exciting!" +#define SOUND_AGGRO_2 10542 +#define SAY_AGGRO_3 "I have longed for a good adventure." +#define SOUND_AGGRO_3 10543 + +#define SAY_SLAY_1 "It has been... entertaining." +#define SOUND_SLAY_1 10544 +#define SAY_SLAY_2 "And now we part company." +#define SOUND_SLAY_2 10545 + +#define SAY_SUMMON "I have such fascinating things to show you." +#define SOUND_SUMMON 10540 + +#define SAY_DEAD "I must bid you... farewell." +#define SOUND_DEAD 10546 + +#define SPELL_BLINK 34605 +#define SPELL_FROSTBOLT 32364 +#define SPELL_FIREBALL 32363 +#define SPELL_FROSTNOVA 32365 + +#define SPELL_ETHEREAL_BEACON 32371 // Summon 18431 +#define SPELL_ETHEREAL_BEACON_VISUAL 32368 + +#define ENTRY_BEACON 18431 + +struct MANGOS_DLL_DECL boss_nexusprince_shaffarAI : public ScriptedAI +{ + boss_nexusprince_shaffarAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Blink_Timer; + uint32 Beacon_Timer; + uint32 FireBall_Timer; + uint32 Frostbolt_Timer; + uint32 FrostNova_Timer; + + bool HasTaunted; + bool CanBlink; + + void Reset() + { + Blink_Timer = 1500; + Beacon_Timer = 10000; + FireBall_Timer = 8000; + Frostbolt_Timer = 4000; + FrostNova_Timer = 15000; + + HasTaunted = false; + CanBlink = false; + } + + void MoveInLineOfSight(Unit *who) + { + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if( !HasTaunted && m_creature->IsWithinDistInMap(who, 100.0) ) + { + DoYell(SAY_INTRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_INTRO); + HasTaunted = true; + } + + if (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) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + break; + case 1: + DoYell(SAY_AGGRO_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + break; + case 2: + DoYell(SAY_AGGRO_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + break; + } + } + + void JustSummoned(Creature *summoned) + { + if( summoned->GetEntry() == ENTRY_BEACON ) + { + summoned->CastSpell(summoned,SPELL_ETHEREAL_BEACON_VISUAL,false); + + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + summoned->AI()->AttackStart(target); + } + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_1); + break; + case 1: + DoYell(SAY_SLAY_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEAD, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEAD); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( FrostNova_Timer < diff ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(true); + + DoCast(m_creature,SPELL_FROSTNOVA); + FrostNova_Timer = 17500 + rand()%7500; + CanBlink = true; + }else FrostNova_Timer -= diff; + + if( Frostbolt_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_FROSTBOLT); + Frostbolt_Timer = 4500 + rand()%1500; + }else Frostbolt_Timer -= diff; + + if( FireBall_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_FIREBALL); + FireBall_Timer = 4500 + rand()%1500; + }else FireBall_Timer -= diff; + + if( CanBlink ) + { + if( Blink_Timer < diff ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(true); + + DoCast(m_creature,SPELL_BLINK); + Blink_Timer = 1000 + rand()%1500; + CanBlink = false; + }else Blink_Timer -= diff; + } + + if( Beacon_Timer < diff) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(true); + + if( !urand(0,3) ) + { + DoYell(SAY_SUMMON, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SUMMON); + } + DoCast(m_creature,SPELL_ETHEREAL_BEACON); + + Beacon_Timer = 10000; + }else Beacon_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_nexusprince_shaffar(Creature *_Creature) +{ + return new boss_nexusprince_shaffarAI (_Creature); +} + +#define SPELL_ARCANE_BOLT 15254 +#define SPELL_ETHEREAL_APPRENTICE 32372 // Summon 18430 + +struct MANGOS_DLL_DECL mob_ethereal_beaconAI : public ScriptedAI +{ + mob_ethereal_beaconAI(Creature *c) : ScriptedAI(c) + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + Reset(); + } + + bool HeroicMode; + bool CanEvade; + uint32 Apprentice_Timer; + uint32 ArcaneBolt_Timer; + + void Reset() + { + if( CanEvade ) + m_creature->SetVisibility(VISIBILITY_OFF); + + CanEvade = false; + Apprentice_Timer = (HeroicMode ? 10000 : 20000); + ArcaneBolt_Timer = 1000; + } + + void Aggro(Unit *who) + { + } + + void JustSummoned(Creature *summoned) + { + summoned->AI()->AttackStart(m_creature->getVictim()); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( ArcaneBolt_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_ARCANE_BOLT); + ArcaneBolt_Timer = 2000 + rand()%2500; + }else ArcaneBolt_Timer -= diff; + + if( Apprentice_Timer < diff ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(true); + + m_creature->CastSpell(m_creature,SPELL_ETHEREAL_APPRENTICE,true); + if( m_creature->GetOwner() ) + ((Pet*)m_creature)->SetDuration(0); + CanEvade = true; + }else Apprentice_Timer -= diff; + + if( CanEvade ) + EnterEvadeMode(); + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_ethereal_beacon(Creature *_Creature) +{ + return new mob_ethereal_beaconAI (_Creature); +} + +void AddSC_boss_nexusprince_shaffar() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_nexusprince_shaffar"; + newscript->GetAI = GetAI_boss_nexusprince_shaffar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_ethereal_beacon"; + newscript->GetAI = GetAI_mob_ethereal_beacon; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_pandemonius.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_pandemonius.cpp new file mode 100644 index 00000000000..25f26289e53 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_pandemonius.cpp @@ -0,0 +1,159 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Pandemonius +SD%Complete: 75 +SDComment: Not known how void blast is done (amount of rapid cast seems to be related to players in party). All mobs remaining in surrounding area should aggro when engaged. +SDCategory: Auchindoun, Mana Tombs +EndScriptData */ + +#include "precompiled.h" + +#define SAY_AGGRO_1 "I will feed on your soul." +#define SOUND_AGGRO_1 10561 +#define SAY_AGGRO_2 "So... full of life!" +#define SOUND_AGGRO_2 10562 +#define SAY_AGGRO_3 "Do not... resist." +#define SOUND_AGGRO_3 10563 + +#define SAY_KILL_1 "Yes! I am... empowered!" +#define SOUND_KILL_1 10564 +#define SAY_KILL_2 "More... I must have more!" +#define SOUND_KILL_2 10565 + +#define SAY_DEATH "To the void... once... more.." +#define SOUND_DEATH 10566 + +#define EMOTE_DARK_SHELL "shifts into the void..." + +#define SPELL_VOID_BLAST 32325 +#define H_SPELL_VOID_BLAST 38760 +#define SPELL_DARK_SHELL 32358 +#define H_SPELL_DARK_SHELL 38759 + +struct MANGOS_DLL_DECL boss_pandemoniusAI : public ScriptedAI +{ + boss_pandemoniusAI(Creature *c) : ScriptedAI(c) + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + Reset(); + } + + bool HeroicMode; + uint32 VoidBlast_Timer; + uint32 DarkShell_Timer; + uint32 VoidBlast_Counter; + + void Reset() + { + VoidBlast_Timer = 30000; + DarkShell_Timer = 20000; + VoidBlast_Counter = 0; + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_1); + break; + case 1: + DoYell(SAY_KILL_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_2); + break; + } + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + break; + case 1: + DoYell(SAY_AGGRO_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + break; + case 2: + DoYell(SAY_AGGRO_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + break; + } + + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( VoidBlast_Timer < diff ) + { + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0) ) + { + DoCast(target,HeroicMode ? H_SPELL_VOID_BLAST : SPELL_VOID_BLAST); + VoidBlast_Timer = 500; + ++VoidBlast_Counter; + } + + if( VoidBlast_Counter == 5 ) + { + VoidBlast_Timer = 25000+rand()%10000; + VoidBlast_Counter = 0; + } + }else VoidBlast_Timer -= diff; + + if( !VoidBlast_Counter ) + { + if( DarkShell_Timer < diff ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(true); + + DoTextEmote(EMOTE_DARK_SHELL,NULL,true); + DoCast(m_creature,HeroicMode ? H_SPELL_DARK_SHELL : SPELL_DARK_SHELL); + DarkShell_Timer = 20000; + }else DarkShell_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_pandemonius(Creature *_Creature) +{ + return new boss_pandemoniusAI (_Creature); +} + +void AddSC_boss_pandemonius() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_pandemonius"; + newscript->GetAI = GetAI_boss_pandemonius; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_darkweaver_syth.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_darkweaver_syth.cpp new file mode 100644 index 00000000000..2be88f661af --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_darkweaver_syth.cpp @@ -0,0 +1,441 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Darkweaver_Syth +SD%Complete: 85 +SDComment: Shock spells/times need more work. Heroic not implemented. +SDCategory: Auchindoun, Sethekk Halls +EndScriptData */ + +#include "precompiled.h" + +#define SAY_SUMMON "I have pets....of my own!" +#define SOUND_SUMMON 10502 + +#define SAY_AGGRO_1 "Hrrmm.. Time to.. hrrm.. make my move." +#define SOUND_AGGRO_1 10503 +#define SAY_AGGRO_2 "Nice pets..hrm.. Yes! " +#define SOUND_AGGRO_2 10504 +#define SAY_AGGRO_3 "Nice pets have.. weapons. No so....nice." +#define SOUND_AGGRO_3 10505 + +#define SAY_SLAY_1 "Death.. meeting life is.. " +#define SOUND_SLAY_1 10506 +#define SAY_SLAY_2 "Uhn.. Be free.." +#define SOUND_SLAY_2 10507 + +#define SAY_DEATH "No more life..hrm. No more pain. " +#define SOUND_DEATH 10508 + +#define SPELL_FROST_SHOCK 37865 +#define SPELL_FLAME_SHOCK 34354 +#define SPELL_SHADOW_SHOCK 30138 +#define SPELL_ARCANE_SHOCK 37132 + +#define SPELL_CHAIN_LIGHTNING 39945 + +#define SPELL_SUMMON_SYTH_FIRE 33537 // Spawns 19203 +#define SPELL_SUMMON_SYTH_ARCANE 33538 // Spawns 19205 +#define SPELL_SUMMON_SYTH_FROST 33539 // Spawns 19204 +#define SPELL_SUMMON_SYTH_SHADOW 33540 // Spawns 19206 + +#define SPELL_FLAME_BUFFET 33526 +#define H_SPELL_FLAME_BUFFET 38141 +#define SPELL_ARCANE_BUFFET 33527 +#define H_SPELL_ARCANE_BUFFET 38138 +#define SPELL_FROST_BUFFET 33528 +#define H_SPELL_FROST_BUFFET 38142 +#define SPELL_SHADOW_BUFFET 33529 +#define H_SPELL_SHADOW_BUFFET 38143 + +struct MANGOS_DLL_DECL boss_darkweaver_sythAI : public ScriptedAI +{ + boss_darkweaver_sythAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 flameshock_timer; + uint32 arcaneshock_timer; + uint32 frostshock_timer; + uint32 shadowshock_timer; + uint32 chainlightning_timer; + + bool summon90; + bool summon50; + bool summon10; + + void Reset() + { + flameshock_timer = 2000; + arcaneshock_timer = 4000; + frostshock_timer = 6000; + shadowshock_timer = 8000; + chainlightning_timer = 15000; + + summon90 = false; + summon50 = false; + summon10 = false; + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + break; + case 1: + DoYell(SAY_AGGRO_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + break; + case 2: + DoYell(SAY_AGGRO_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + void KilledUnit(Unit* victim) + { + if (rand()%2) + return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_1); + break; + case 1: + DoYell(SAY_SLAY_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_2); + break; + } + } + + void JustSummoned(Creature *summoned) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + summoned->AI()->AttackStart(target); + + } + + void SythSummoning() + { + DoYell(SAY_SUMMON, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SUMMON); + + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(false); + + DoCast(m_creature,SPELL_SUMMON_SYTH_ARCANE,true); //front + DoCast(m_creature,SPELL_SUMMON_SYTH_FIRE,true); //back + DoCast(m_creature,SPELL_SUMMON_SYTH_FROST,true); //left + DoCast(m_creature,SPELL_SUMMON_SYTH_SHADOW,true); //right + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 90) && !summon90) + { + SythSummoning(); + summon90 = true; + } + + if( ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 50) && !summon50) + { + SythSummoning(); + summon50 = true; + } + + if( ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 10) && !summon10) + { + SythSummoning(); + summon10 = true; + } + + if( flameshock_timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_FLAME_SHOCK); + + flameshock_timer = 10000 + rand()%5000; + }else flameshock_timer -= diff; + + if( arcaneshock_timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_ARCANE_SHOCK); + + arcaneshock_timer = 10000 + rand()%5000; + }else arcaneshock_timer -= diff; + + if( frostshock_timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_FROST_SHOCK); + + frostshock_timer = 10000 + rand()%5000; + }else frostshock_timer -= diff; + + if( shadowshock_timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_SHADOW_SHOCK); + + shadowshock_timer = 10000 + rand()%5000; + }else shadowshock_timer -= diff; + + if( chainlightning_timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_CHAIN_LIGHTNING); + + chainlightning_timer = 25000; + }else chainlightning_timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_darkweaver_syth(Creature *_Creature) +{ + return new boss_darkweaver_sythAI (_Creature); +} + +/* ELEMENTALS */ + +struct MANGOS_DLL_DECL mob_syth_fireAI : public ScriptedAI +{ + mob_syth_fireAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 flameshock_timer; + uint32 flamebuffet_timer; + + void Reset() + { + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, true); + flameshock_timer = 2500; + flamebuffet_timer = 5000; + } + + void Aggro(Unit *who) { } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(flameshock_timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_FLAME_SHOCK); + + flameshock_timer = 5000; + }else flameshock_timer -= diff; + + if(flamebuffet_timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_FLAME_BUFFET); + + flamebuffet_timer = 5000; + }else flamebuffet_timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_syth_fire(Creature *_Creature) +{ + return new mob_syth_fireAI (_Creature); +} + +struct MANGOS_DLL_DECL mob_syth_arcaneAI : public ScriptedAI +{ + mob_syth_arcaneAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 arcaneshock_timer; + uint32 arcanebuffet_timer; + + void Reset() + { + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, true); + arcaneshock_timer = 2500; + arcanebuffet_timer = 5000; + } + + void Aggro(Unit *who) { } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(arcaneshock_timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_ARCANE_SHOCK); + + arcaneshock_timer = 5000; + }else arcaneshock_timer -= diff; + + if(arcanebuffet_timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_ARCANE_BUFFET); + + arcanebuffet_timer = 5000; + }else arcanebuffet_timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_syth_arcane(Creature *_Creature) +{ + return new mob_syth_arcaneAI (_Creature); +} + +struct MANGOS_DLL_DECL mob_syth_frostAI : public ScriptedAI +{ + mob_syth_frostAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 frostshock_timer; + uint32 frostbuffet_timer; + + void Reset() + { + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true); + frostshock_timer = 2500; + frostbuffet_timer = 5000; + } + + void Aggro(Unit *who) { } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(frostshock_timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_FROST_SHOCK); + + frostshock_timer = 5000; + }else frostshock_timer -= diff; + + if(frostbuffet_timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_FROST_BUFFET); + + frostbuffet_timer = 5000; + }else frostbuffet_timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_syth_frost(Creature *_Creature) +{ + return new mob_syth_frostAI (_Creature); +} + +struct MANGOS_DLL_DECL mob_syth_shadowAI : public ScriptedAI +{ + mob_syth_shadowAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 shadowshock_timer; + uint32 shadowbuffet_timer; + + void Reset() + { + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, true); + shadowshock_timer = 2500; + shadowbuffet_timer = 5000; + } + + void Aggro(Unit *who) { } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(shadowshock_timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_SHADOW_SHOCK); + + shadowshock_timer = 5000; + }else shadowshock_timer -= diff; + + if(shadowbuffet_timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_SHADOW_BUFFET); + + shadowbuffet_timer = 5000; + }else shadowbuffet_timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_syth_shadow(Creature *_Creature) +{ + return new mob_syth_shadowAI (_Creature); +} + +void AddSC_boss_darkweaver_syth() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_darkweaver_syth"; + newscript->GetAI = GetAI_boss_darkweaver_syth; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_syth_fire"; + newscript->GetAI = GetAI_mob_syth_arcane; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_syth_arcane"; + newscript->GetAI = GetAI_mob_syth_arcane; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_syth_frost"; + newscript->GetAI = GetAI_mob_syth_frost; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_syth_shadow"; + newscript->GetAI = GetAI_mob_syth_shadow; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_tailonking_ikiss.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_tailonking_ikiss.cpp new file mode 100644 index 00000000000..4168458685d --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_tailonking_ikiss.cpp @@ -0,0 +1,256 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Talon_King_Ikiss +SD%Complete: 80 +SDComment: Heroic supported. Some details missing, but most are spell related. +SDCategory: Auchindoun, Sethekk Halls +EndScriptData */ + +#include "precompiled.h" +#include "def_sethekk_halls.h" + +#define SAY_INTRO "..Trinkets yes pretty Trinkets....power, great power...power in Trinkets.." +#define SOUND_INTRO 10557 + +#define SAY_AGGRO_1 "You make war on Ikiss?.." +#define SOUND_AGGRO_1 10554 +#define SAY_AGGRO_2 "Ikiss cut you pretty....slice you. Yes!" +#define SOUND_AGGRO_2 10555 +#define SAY_AGGRO_3 "No escape for....for you" +#define SOUND_AGGRO_3 10556 + +#define SAY_SLAY_1 "You die....stay away from Trinkets" +#define SOUND_SLAY_1 10558 +#define SAY_SLAY_2 "" +#define SOUND_SLAY_2 10559 + +#define SAY_DEATH "Ikiss will not....die" +#define SOUND_DEATH 10560 + +#define EMOTE_ARCANE_EXP "begins to channel arcane energy..." + +#define SPELL_BLINK 38194 +#define SPELL_BLINK_TELEPORT 38203 +#define SPELL_MANA_SHIELD 38151 +#define SPELL_ARCANE_BUBBLE 9438 +#define H_SPELL_SLOW 35032 + +#define SPELL_POLYMORPH 38245 +#define H_SPELL_POLYMORPH 43309 + +#define SPELL_ARCANE_VOLLEY 35059 +#define H_SPELL_ARCANE_VOLLEY 40424 + +#define SPELL_ARCANE_EXPLOSION 38197 +#define H_SPELL_ARCANE_EXPLOSION 40425 + +struct MANGOS_DLL_DECL boss_talon_king_ikissAI : public ScriptedAI +{ + boss_talon_king_ikissAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + bool HeroicMode; + + uint32 ArcaneVolley_Timer; + uint32 Sheep_Timer; + uint32 Blink_Timer; + uint32 Slow_Timer; + + bool ManaShield; + bool Blink; + bool Intro; + + void Reset() + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + + ArcaneVolley_Timer = 5000; + Sheep_Timer = 8000; + Blink_Timer = 35000; + Slow_Timer = 15000+rand()%15000; + Blink = false; + Intro = false; + ManaShield = false; + } + + void MoveInLineOfSight(Unit *who) + { + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if(!Intro && m_creature->IsWithinDistInMap(who, 100)) + { + Intro = true; + DoYell(SAY_INTRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_INTRO); + } + + if (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) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + break; + + case 1: + DoYell(SAY_AGGRO_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + break; + + case 2: + DoYell(SAY_AGGRO_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + + if( pInstance ) + pInstance->SetData(DATA_IKISSDOOREVENT, DONE); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_1); + break; + + case 1: + DoYell(SAY_SLAY_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_2); + break; + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( Blink ) + { + DoCast(m_creature,HeroicMode ? H_SPELL_ARCANE_EXPLOSION : SPELL_ARCANE_EXPLOSION); + m_creature->CastSpell(m_creature,SPELL_ARCANE_BUBBLE,true); + Blink = false; + } + + if( ArcaneVolley_Timer < diff ) + { + DoCast(m_creature,HeroicMode ? H_SPELL_ARCANE_VOLLEY : SPELL_ARCANE_VOLLEY); + ArcaneVolley_Timer = 10000+rand()%5000; + }else ArcaneVolley_Timer -= diff; + + if( Sheep_Timer < diff ) + { + //second top aggro target in normal, random target in heroic correct? + Unit *target = NULL; + if( HeroicMode ? target = SelectUnit(SELECT_TARGET_RANDOM,0) : target = SelectUnit(SELECT_TARGET_TOPAGGRO,1) ) + DoCast(target,HeroicMode ? H_SPELL_POLYMORPH : SPELL_POLYMORPH); + Sheep_Timer = 15000+rand()%2500; + }else Sheep_Timer -= diff; + + //may not be correct time to cast + if( !ManaShield && ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 20) ) + { + DoCast(m_creature,SPELL_MANA_SHIELD); + ManaShield = true; + } + + if( HeroicMode ) + { + if( Slow_Timer < diff ) + { + DoCast(m_creature,H_SPELL_SLOW); + Slow_Timer = 15000+rand()%25000; + }else Slow_Timer -= diff; + } + + if( Blink_Timer < diff ) + { + DoTextEmote(EMOTE_ARCANE_EXP,NULL,true); + + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(false); + + //Spell doesn't work, but we use for visual effect at least + DoCast(target,SPELL_BLINK); + + float X = target->GetPositionX(); + float Y = target->GetPositionY(); + float Z = target->GetPositionZ(); + + m_creature->Relocate(X,Y,Z); + m_creature->SendMonsterMove(X, Y, Z, 0, 0, 0); + + DoCast(target,SPELL_BLINK_TELEPORT); + Blink = true; + } + Blink_Timer = 35000+rand()%5000; + }else Blink_Timer -= diff; + + if( !Blink ) + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_talon_king_ikiss(Creature *_Creature) +{ + return new boss_talon_king_ikissAI (_Creature); +} + +void AddSC_boss_talon_king_ikiss() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_talon_king_ikiss"; + newscript->GetAI = GetAI_boss_talon_king_ikiss; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/def_sethekk_halls.h b/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/def_sethekk_halls.h new file mode 100644 index 00000000000..f609ecac1f2 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/def_sethekk_halls.h @@ -0,0 +1,9 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SETHEKK_HALLS_H +#define DEF_SETHEKK_HALLS_H + +#define DATA_IKISSDOOREVENT 1 +#endif diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/instance_sethekk_halls.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/instance_sethekk_halls.cpp new file mode 100644 index 00000000000..980717653c6 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/instance_sethekk_halls.cpp @@ -0,0 +1,74 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance - Sethekk Halls +SD%Complete: 50 +SDComment: Instance Data for Sethekk Halls instance +SDCategory: Auchindoun, Sethekk Halls +EndScriptData */ + +#include "precompiled.h" +#include "def_sethekk_halls.h" + +#define IKISS_DOOR 177203 + +struct MANGOS_DLL_DECL instance_sethekk_halls : public ScriptedInstance +{ + instance_sethekk_halls(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + GameObject *IkissDoor; + + void Initialize() + { + IkissDoor = NULL; + } + + void OnObjectCreate(GameObject *go) + { + switch(go->GetEntry()) + { + case IKISS_DOOR: + IkissDoor = go; + break; + } + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_IKISSDOOREVENT: + if( IkissDoor ) + IkissDoor->SetGoState(0); + break; + } + } +}; + +InstanceData* GetInstanceData_instance_sethekk_halls(Map* map) +{ + return new instance_sethekk_halls(map); +} + +void AddSC_instance_sethekk_halls() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_sethekk_halls"; + newscript->GetInstanceData = GetInstanceData_instance_sethekk_halls; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp new file mode 100644 index 00000000000..b575461dfaa --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp @@ -0,0 +1,223 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Ambassador_Hellmaw +SD%Complete: 75 +SDComment: Waypoints after Intro not implemented. Enrage spell missing/not known +SDCategory: Auchindoun, Shadow Labyrinth +EndScriptData */ + +#include "precompiled.h" +#include "def_shadow_labyrinth.h" + +#define SAY_INTRO "Infidels have invaded the sanctuary! Sniveling pests...You have yet to learn the true meaning of agony!" +#define SOUND_INTRO 10473 + +#define SAY_AGGRO1 "Pathetic mortals! You will pay dearly!" +#define SOUND_AGGRO1 10475 +#define SAY_AGGRO2 "I will break you!" +#define SOUND_AGGRO2 10476 +#define SAY_AGGRO3 "Finally! Something to relieve the tedium!" +#define SOUND_AGGRO3 10477 + +#define SAY_HELP "Aid me, you fools, before it's too late!" +#define SOUND_HELP 10474 + +#define SAY_SLAY1 "Do you fear death?" +#define SOUND_SLAY1 10478 +#define SAY_SLAY2 "This is the part I enjoy most." +#define SOUND_SLAY2 10479 + +#define SAY_DEATH "Do not...grow...overconfident, mortal." +#define SOUND_DEATH 10480 + +#define SPELL_BANISH 30231 +#define SPELL_CORROSIVE_ACID 23313 +#define SPELL_FEAR 33547 +#define SPELL_ENRAGE 0 //need to find proper spell + +struct MANGOS_DLL_DECL boss_ambassador_hellmawAI : public ScriptedAI +{ + boss_ambassador_hellmawAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + bool HeroicMode; + + uint32 EventCheck_Timer; + uint32 CorrosiveAcid_Timer; + uint32 Fear_Timer; + uint32 Enrage_Timer; + bool Intro; + bool IsBanished; + + void Reset() + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + + EventCheck_Timer = 5000; + CorrosiveAcid_Timer = 25000; + Fear_Timer = 40000; + Enrage_Timer = 180000; + Intro = false; + IsBanished = false; + + if( pInstance ) + { + if( pInstance->GetData(TYPE_HELLMAW) == NOT_STARTED ) + { + DoCast(m_creature,SPELL_BANISH); + IsBanished = true; + } + else pInstance->SetData(TYPE_HELLMAW,FAIL); + + if( pInstance->GetData(TYPE_OVERSEER) == DONE ) + { + if( m_creature->HasAura(SPELL_BANISH,0) ) + m_creature->RemoveAurasDueToSpell(SPELL_BANISH); + Intro = true; + } + } + } + + void MovementInform(uint32 type, uint32 id) + { + if( type != POINT_MOTION_TYPE ) + return; + } + + void DoIntro() + { + DoYell(SAY_INTRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_INTRO); + + if( m_creature->HasAura(SPELL_BANISH,0) ) + m_creature->RemoveAurasDueToSpell(SPELL_BANISH); + + IsBanished = false; + Intro = true; + + if( pInstance ) + pInstance->SetData(TYPE_HELLMAW, IN_PROGRESS); + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO3); + break; + } + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if( pInstance ) + pInstance->SetData(TYPE_HELLMAW, DONE); + } + + void UpdateAI(const uint32 diff) + { + if( !Intro ) + { + if( EventCheck_Timer < diff ) + { + if( pInstance ) + { + if( pInstance->GetData(TYPE_OVERSEER) == DONE ) + DoIntro(); + } + EventCheck_Timer = 5000; + }else EventCheck_Timer -= diff; + } + + if( !InCombat && !IsBanished ) + { + //this is where we add MovePoint() + //DoWhine("I haz no mount!", LANG_UNIVERSAL, NULL); + } + + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( CorrosiveAcid_Timer < diff ) + { + DoCast(m_creature,SPELL_CORROSIVE_ACID); + CorrosiveAcid_Timer = 25000; + }else CorrosiveAcid_Timer -= diff; + + if( Fear_Timer < diff ) + { + DoCast(m_creature,SPELL_FEAR); + Fear_Timer = 35000; + }else Fear_Timer -= diff; + + /*if( HeroicMode ) + { + if( Enrage_Timer < diff ) + { + DoCast(m_creature,SPELL_ENRAGE); + }else Enrage_Timer -= diff; + }*/ + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_ambassador_hellmaw(Creature *_Creature) +{ + return new boss_ambassador_hellmawAI (_Creature); +} + +void AddSC_boss_ambassador_hellmaw() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_ambassador_hellmaw"; + newscript->GetAI = GetAI_boss_ambassador_hellmaw; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp new file mode 100644 index 00000000000..0a8c446b15c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp @@ -0,0 +1,191 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Blackheart_the_Inciter +SD%Complete: 75 +SDComment: Incite Chaos not functional since core lacks Mind Control support +SDCategory: Auchindoun, Shadow Labyrinth +EndScriptData */ + +#include "precompiled.h" +#include "def_shadow_labyrinth.h" + +#define SPELL_INCITE_CHAOS 33676 +#define SPELL_INCITE_CHAOS_B 33684 //debuff applied to each member of party +#define SPELL_CHARGE 33709 +#define SPELL_WAR_STOMP 33707 + +#define SAY_AGGRO1 "You be dead people!" +#define SAY_AGGRO2 "Time to kill!" +#define SAY_AGGRO3 "I see dead people!" +#define SAY_SLAY1 "No coming back for you!" +#define SAY_SLAY2 "Nice try!" +#define SAY_SLAY3 "Now you gone for good!" +#define SAY_DEATH "This...no...good.." + +#define SOUND_AGGRO1 10498 +#define SOUND_AGGRO2 10497 +#define SOUND_AGGRO3 10488 +#define SOUND_SLAY1 10489 +#define SOUND_SLAY2 10490 +#define SOUND_SLAY3 10499 +#define SOUND_DEATH 10491 + +struct MANGOS_DLL_DECL boss_blackheart_the_inciterAI : public ScriptedAI +{ + boss_blackheart_the_inciterAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + bool InciteChaos; + uint32 InciteChaos_Timer; + uint32 InciteChaosWait_Timer; + uint32 Charge_Timer; + uint32 Knockback_Timer; + + void Reset() + { + InciteChaos = false; + InciteChaos_Timer = 20000; + InciteChaosWait_Timer = 15000; + Charge_Timer = 5000; + Knockback_Timer = 15000; + + if( pInstance ) + pInstance->SetData(DATA_BLACKHEARTTHEINCITEREVENT, NOT_STARTED); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + case 2: + DoYell(SAY_SLAY3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY3); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if( pInstance ) + pInstance->SetData(DATA_BLACKHEARTTHEINCITEREVENT, DONE); + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO3); + break; + } + + if( pInstance ) + pInstance->SetData(DATA_BLACKHEARTTHEINCITEREVENT, IN_PROGRESS); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( InciteChaos ) + { + if( InciteChaosWait_Timer < diff ) + { + InciteChaos = false; + InciteChaosWait_Timer = 15000; + }else InciteChaosWait_Timer -= diff; + + return; + } + + if( InciteChaos_Timer < diff ) + { + DoCast(m_creature, SPELL_INCITE_CHAOS); + + std::list t_list = m_creature->getThreatManager().getThreatList(); + for( std::list::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr ) + { + Unit* target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + if( target && target->GetTypeId() == TYPEID_PLAYER ) + target->CastSpell(target,SPELL_INCITE_CHAOS_B,true); + } + + DoResetThreat(); + InciteChaos = true; + InciteChaos_Timer = 40000; + return; + }else InciteChaos_Timer -= diff; + + //Charge_Timer + if( Charge_Timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0) ) + DoCast(target, SPELL_CHARGE); + Charge_Timer = 25000; + }else Charge_Timer -= diff; + + //Knockback_Timer + if( Knockback_Timer < diff ) + { + DoCast(m_creature, SPELL_WAR_STOMP); + Knockback_Timer = 20000; + }else Knockback_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_blackheart_the_inciter(Creature *_Creature) +{ + return new boss_blackheart_the_inciterAI (_Creature); +} + +void AddSC_boss_blackheart_the_inciter() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_blackheart_the_inciter"; + newscript->GetAI = GetAI_boss_blackheart_the_inciter; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp new file mode 100644 index 00000000000..5e278c91fc5 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp @@ -0,0 +1,294 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Grandmaster_Vorpil +SD%Complete: 75 +SDComment: Despawn all summoned on death not implemented. Void Traveler effects not implemented. +SDCategory: Auchindoun, Shadow Labyrinth +EndScriptData */ + +#include "precompiled.h" +#include "def_shadow_labyrinth.h" + +#define SPELL_DRAW_SHADOWS 33563 +#define SPELL_VOID_PORTAL_A 33566 //spell only summon one unit, but we use it for the visual effect and summon the 4 other portals manual way(only one spell exist) +#define SPELL_VOID_PORTAL_VISUAL 33569 +#define SPELL_SHADOW_BOLT_VOLLEY 32963 +#define SPELL_SUMMON_VOIDWALKER_A 33582 +#define SPELL_SUMMON_VOIDWALKER_B 33583 +#define SPELL_SUMMON_VOIDWALKER_C 33584 +#define SPELL_SUMMON_VOIDWALKER_D 33585 +#define SPELL_SUMMON_VOIDWALKER_E 33586 +#define SPELL_RAIN_OF_FIRE 33617 +#define H_SPELL_RAIN_OF_FIRE 39363 +#define H_SPELL_BANISH 38791 + +#define SAY_INTRO "Keep your minds focused for the days of reckoning are close at hand. Soon, the destroyer of worlds will return to make good on his promise. Soon the destruction of all that is will begin!" +#define SAY_AGGRO1 "I'll make an offering of your blood!" +#define SAY_AGGRO2 "You'll be a fine example, for the others." +#define SAY_AGGRO3 "Good, a worthy sacrifice." +#define SAY_HELP "Come to my aid, heed your master now!" +#define SAY_SLAY1 "I serve with pride." +#define SAY_SLAY2 "Your death is for the greater cause!" +#define SAY_DEATH "I give my life... Gladly." + +#define SOUND_INTRO 10522 +#define SOUND_AGGRO1 10524 +#define SOUND_AGGRO2 10525 +#define SOUND_AGGRO3 10526 +#define SOUND_HELP 10523 +#define SOUND_SLAY1 10527 +#define SOUND_SLAY2 10528 +#define SOUND_DEATH 10529 + +#define ENTRY_VOID_PORTAL 19224 +#define ENTRY_VOID_TRAVELER 19226 + +#define LOCX -253.06f +#define LOCY -264.02f +#define LOCZ 17.08 + +struct MANGOS_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI +{ + boss_grandmaster_vorpilAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + bool HeroicMode; + + uint32 ShadowBoltVolley_Timer; + uint32 DrawShadows_Timer; + uint32 Teleport_Timer; + uint32 VoidTraveler_Timer; + uint32 Banish_Timer; + bool Intro; + bool Teleport; + + void Reset() + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + + ShadowBoltVolley_Timer = 15000; + DrawShadows_Timer = 40000; + Teleport_Timer = 1000; + VoidTraveler_Timer = 20000; + Banish_Timer = 25000; + Intro = false; + Teleport = false; + + if( pInstance ) + pInstance->SetData(DATA_GRANDMASTERVORPILEVENT, NOT_STARTED); + } + + void MoveInLineOfSight(Unit *who) + { + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + //not sure about right radius + if(!Intro && m_creature->IsWithinDistInMap(who, 50)) + { + DoYell(SAY_INTRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_INTRO); + DoCast(m_creature, SPELL_VOID_PORTAL_A,true); + m_creature->SummonCreature(ENTRY_VOID_PORTAL,-262.40,-229.57,17.08,0,TEMPSUMMON_CORPSE_DESPAWN,0); + m_creature->SummonCreature(ENTRY_VOID_PORTAL,-260.35,-297.56,17.08,0,TEMPSUMMON_CORPSE_DESPAWN,0); + m_creature->SummonCreature(ENTRY_VOID_PORTAL,-292.05,-270.37,12.68,0,TEMPSUMMON_CORPSE_DESPAWN,0); + m_creature->SummonCreature(ENTRY_VOID_PORTAL,-301.64,-255.97,12.68,0,TEMPSUMMON_CORPSE_DESPAWN,0); + Intro = true; + } + + if (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) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO3); + break; + } + + if( pInstance ) + pInstance->SetData(DATA_GRANDMASTERVORPILEVENT, IN_PROGRESS); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustSummoned(Creature *summoned) + { + if( summoned->GetEntry() == ENTRY_VOID_TRAVELER ) + { + summoned->GetMotionMaster()->MoveChase(m_creature); + summoned->SetSpeed(MOVE_WALK,0.8,true); + } + + if( summoned->GetEntry() == ENTRY_VOID_PORTAL ) + summoned->CastSpell(summoned,SPELL_VOID_PORTAL_VISUAL,true); + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if( pInstance ) + pInstance->SetData(DATA_GRANDMASTERVORPILEVENT, DONE); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( Teleport ) + { + if( Teleport_Timer <= diff ) + { + m_creature->Relocate(LOCX,LOCY,LOCZ); + m_creature->SendMonsterMove(LOCX,LOCY,LOCZ,0,true,0); + + float ranX = LOCX; + float ranY = LOCY; + float ranZ = LOCZ; + + std::list t_list = m_creature->getThreatManager().getThreatList(); + for( std::list::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr ) + { + Unit* target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + if( target && target->GetTypeId() == TYPEID_PLAYER ) + { + target->GetRandomPoint(LOCX,LOCY,LOCZ,3.0,ranX,ranY,ranZ); + DoTeleportPlayer(target,ranX,ranY,ranZ,m_creature->GetAngle(m_creature->GetPositionX(),m_creature->GetPositionY())); + } + } + Teleport = false; + + if( HeroicMode ) DoCast(m_creature->getVictim(), H_SPELL_RAIN_OF_FIRE); + else DoCast(m_creature->getVictim(), SPELL_RAIN_OF_FIRE); + + Teleport_Timer = 1000; + }else Teleport_Timer -= diff; + } + + if( ShadowBoltVolley_Timer < diff ) + { + DoCast(m_creature->getVictim(), SPELL_SHADOW_BOLT_VOLLEY); + ShadowBoltVolley_Timer = 30000; + }else ShadowBoltVolley_Timer -= diff; + + if( DrawShadows_Timer < diff ) + { + DoCast(m_creature,SPELL_DRAW_SHADOWS); + DrawShadows_Timer = 35000; + Teleport = true; + }else DrawShadows_Timer -= diff; + + if( VoidTraveler_Timer < diff ) + { + DoYell(SAY_HELP, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_HELP); + + switch(rand()%5) + { + case 0: + DoCast(m_creature,SPELL_SUMMON_VOIDWALKER_A,true); + break; + case 1: + DoCast(m_creature,SPELL_SUMMON_VOIDWALKER_B,true); + break; + case 2: + DoCast(m_creature,SPELL_SUMMON_VOIDWALKER_C,true); + break; + case 3: + DoCast(m_creature,SPELL_SUMMON_VOIDWALKER_D,true); + break; + case 4: + DoCast(m_creature,SPELL_SUMMON_VOIDWALKER_E,true); + break; + } + //faster rate when below (X) health? + VoidTraveler_Timer = 35000; + }else VoidTraveler_Timer -= diff; + + if( HeroicMode ) + { + if( Banish_Timer < diff ) + { + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1) ) + DoCast(target,H_SPELL_BANISH); + Banish_Timer = 35000; + }else Banish_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_grandmaster_vorpil(Creature *_Creature) +{ + return new boss_grandmaster_vorpilAI (_Creature); +} + +void AddSC_boss_grandmaster_vorpil() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_grandmaster_vorpil"; + newscript->GetAI = GetAI_boss_grandmaster_vorpil; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_murmur.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_murmur.cpp new file mode 100644 index 00000000000..81aa6ee170d --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_murmur.cpp @@ -0,0 +1,192 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Murmur +SD%Complete: 75 +SDComment: Database should have `RegenHealth`=0 to prevent regen. Also, his shockwave triggered after magnetic pull may be incorrect. Murmur's Touch does not work properly. +SDCategory: Auchindoun, Shadow Labyrinth +EndScriptData */ + +#include "precompiled.h" + +#define EMOTE_SONIC_BOOM "draws energy from the air." + +#define SPELL_MAGNETIC_PULL 33689 +#define SPELL_SONIC_BOOM_PRE 33923 +#define SPELL_SONIC_BOOM_CAST 38795 +#define SPELL_MURMURS_TOUCH 33711 +#define SPELL_RESONANCE 33657 +#define SPELL_SHOCKWAVE 33686 + +struct MANGOS_DLL_DECL boss_murmurAI : public ScriptedAI +{ + boss_murmurAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 SonicBoom_Timer; + uint32 MurmursTouch_Timer; + uint32 Resonance_Timer; + uint32 MagneticPull_Timer; + bool CanSonicBoom; + bool CanShockWave; + uint64 pTarget; + + void Reset() + { + SonicBoom_Timer = 30000; + MurmursTouch_Timer = 20000; + Resonance_Timer = 10000; + MagneticPull_Timer = 45000; + CanSonicBoom = false; + CanShockWave = false; + pTarget = 0; + + //database should have `RegenHealth`=0 to prevent regen + uint32 hp = m_creature->GetMaxHealth()*0.4; + if( hp ) + m_creature->SetHealth(hp); + } + + void Aggro(Unit *who) { } + + void AttackStart(Unit* who) + { + if (!who) + return; + + if (who->isTargetableForAttack()) + { + //Begin attack + DoStartAttackNoMovement(who); + + if (!InCombat) + { + Aggro(who); + InCombat = true; + } + } + } + + void MoveInLineOfSight(Unit* who) + { + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if (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) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //SonicBoom_Timer + if(SonicBoom_Timer < diff) + { + if(CanSonicBoom) + { + DoCast(m_creature, SPELL_SONIC_BOOM_CAST,true); + CanSonicBoom = false; + SonicBoom_Timer = 30000; + } + else + { + DoTextEmote(EMOTE_SONIC_BOOM,NULL); + DoCast(m_creature,SPELL_SONIC_BOOM_PRE); + CanSonicBoom = true; + SonicBoom_Timer = 5000; + } + }else SonicBoom_Timer -= diff; + + //MurmursTouch_Timer + if(MurmursTouch_Timer < diff) + { + /*Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if(target) + DoCast(target, SPELL_MURMURS_TOUCH);*/ + DoCast(m_creature, SPELL_MURMURS_TOUCH); + MurmursTouch_Timer = 30000; + }else MurmursTouch_Timer -= diff; + + //Resonance_Timer + if(Resonance_Timer < diff) + { + if( !m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE) ) + DoCast(m_creature->getVictim(), SPELL_RESONANCE); + Resonance_Timer = 5000; + }else Resonance_Timer -= diff; + + //MagneticPull_Timer + if(MagneticPull_Timer < diff) + { + if( !CanShockWave ) + { + if( Unit* temp = SelectUnit(SELECT_TARGET_RANDOM,0) ) + { + if( temp->GetTypeId() == TYPEID_PLAYER ) + { + DoCast(temp, SPELL_MAGNETIC_PULL); + pTarget = temp->GetGUID(); + CanShockWave = true; + } + MagneticPull_Timer = 2500; + } + } + else + { + if( Unit* target = Unit::GetUnit(*m_creature,pTarget) ) + target->CastSpell(target,SPELL_SHOCKWAVE,true); + + MagneticPull_Timer = 35000; + CanShockWave = false; + pTarget = 0; + } + }else MagneticPull_Timer -= diff; + + //no meele if preparing for sonic boom + if(!CanSonicBoom) + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_murmur(Creature *_Creature) +{ + return new boss_murmurAI (_Creature); +} + +void AddSC_boss_murmur() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_murmur"; + newscript->GetAI = GetAI_boss_murmur; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/def_shadow_labyrinth.h b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/def_shadow_labyrinth.h new file mode 100644 index 00000000000..77b0387af16 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/def_shadow_labyrinth.h @@ -0,0 +1,13 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SHADOW_LABYRINTH_H +#define DEF_SHADOW_LABYRINTH_H + +#define TYPE_HELLMAW 1 +#define TYPE_OVERSEER 2 +#define DATA_BLACKHEARTTHEINCITEREVENT 3 +#define DATA_GRANDMASTERVORPILEVENT 4 +#define DATA_MURMUREVENT 5 +#endif diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp new file mode 100644 index 00000000000..ea2c141b2e0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp @@ -0,0 +1,168 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Shadow_Labyrinth +SD%Complete: 85 +SDComment: Some cleanup left along with save +SDCategory: Auchindoun, Shadow Labyrinth +EndScriptData */ + +#include "precompiled.h" +#include "def_shadow_labyrinth.h" + +#define ENCOUNTERS 5 + +#define REFECTORY_DOOR 183296 //door opened when blackheart the inciter dies +#define SCREAMING_HALL_DOOR 183295 //door opened when grandmaster vorpil dies + +/* Shadow Labyrinth encounters: +1 - Ambassador Hellmaw event +2 - Blackheart the Inciter event +3 - Grandmaster Vorpil event +4 - Murmur event +*/ + +struct MANGOS_DLL_DECL instance_shadow_labyrinth : public ScriptedInstance +{ + instance_shadow_labyrinth(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint32 Encounter[ENCOUNTERS]; + + GameObject *RefectoryDoor; + GameObject *ScreamingHallDoor; + uint64 GrandmasterVorpil; + uint32 FelOverseerCount; + + void Initialize() + { + RefectoryDoor = NULL; + ScreamingHallDoor = NULL; + GrandmasterVorpil = 0; + FelOverseerCount = 0; + + for(uint8 i = 0; i < ENCOUNTERS; i++) + Encounter[i] = false; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < ENCOUNTERS; i++) + if(Encounter[i]) return true; + + return false; + } + + void OnObjectCreate(GameObject *go) + { + switch(go->GetEntry()) + { + case REFECTORY_DOOR: + RefectoryDoor = go; + break; + case SCREAMING_HALL_DOOR: + ScreamingHallDoor = go; + break; + } + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case 18732: + GrandmasterVorpil = creature->GetGUID(); + break; + case 18796: + ++FelOverseerCount; + debug_log("SD2: Shadow Labyrinth: counting %u Fel Overseers.",FelOverseerCount); + break; + } + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case TYPE_HELLMAW: + Encounter[0] = data; + break; + + case TYPE_OVERSEER: + if( data != DONE ) + error_log("SD2: Shadow Labyrinth: TYPE_OVERSEER did not expect other data than DONE"); + if( FelOverseerCount ) + { + --FelOverseerCount; + debug_log("SD2: Shadow Labyrinth: %u Fel Overseers left to kill.",FelOverseerCount); + } + if( FelOverseerCount == 0 ) + { + Encounter[1] = DONE; + debug_log("SD2: Shadow Labyrinth: TYPE_OVERSEER == DONE"); + } + break; + + case DATA_BLACKHEARTTHEINCITEREVENT: + if( data == DONE ) + { + if( RefectoryDoor ) + RefectoryDoor->UseDoorOrButton(); + } + Encounter[2] = data; + break; + + case DATA_GRANDMASTERVORPILEVENT: + if( data == DONE ) + { + if( ScreamingHallDoor ) + ScreamingHallDoor->UseDoorOrButton(); + } + Encounter[3] = data; + break; + + case DATA_MURMUREVENT: + Encounter[4] = data; + break; + } + } + + uint32 GetData(uint32 type) + { + switch( type ) + { + case TYPE_HELLMAW: + return Encounter[0]; + case TYPE_OVERSEER: + return Encounter[1]; + } + return false; + } +}; + +InstanceData* GetInstanceData_instance_shadow_labyrinth(Map* map) +{ + return new instance_shadow_labyrinth(map); +} + +void AddSC_instance_shadow_labyrinth() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_shadow_labyrinth"; + newscript->GetInstanceData = GetInstanceData_instance_shadow_labyrinth; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/azshara/azshara.cpp b/src/bindings/scripts/scripts/zone/azshara/azshara.cpp new file mode 100644 index 00000000000..c892847bbc8 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/azshara/azshara.cpp @@ -0,0 +1,164 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Azshara +SD%Complete: 90 +SDComment: Quest support: 2744, 3141, 9364 +SDCategory: Azshara +EndScriptData */ + +/* ContentData +mobs_spitelashes +npc_loramus_thalipedes +EndContentData */ + +#include "precompiled.h" + +/*###### +## mobs_spitelashes +######*/ + +struct MANGOS_DLL_DECL mobs_spitelashesAI : public ScriptedAI +{ + mobs_spitelashesAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 morphtimer; + bool spellhit; + + void Reset() + { + morphtimer = 0; + spellhit = false; + } + + void Aggro(Unit *who) { } + + void SpellHit(Unit *Hitter, const SpellEntry *Spellkind) + { + if( !spellhit && + Hitter->GetTypeId() == TYPEID_PLAYER && + ((Player*)Hitter)->GetQuestStatus(9364) == QUEST_STATUS_INCOMPLETE && + (Spellkind->Id==118 || Spellkind->Id== 12824 || Spellkind->Id== 12825 || Spellkind->Id== 12826) ) + { + spellhit=true; + DoCast(m_creature,29124); //become a sheep + } + } + + void UpdateAI(const uint32 diff) + { + // we mustn't remove the creature in the same round in which we cast the summon spell, otherwise there will be no summons + if( spellhit && morphtimer>=5000 ) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + m_creature->RemoveCorpse(); //you don't see any corpse on off. + EnterEvadeMode(); //spellhit will be set to false + } + // walk 5 seconds before summoning + if( spellhit && morphtimer<5000 ) + { + morphtimer+=diff; + if( morphtimer>=5000 ) + { + DoCast(m_creature,28406); //summon copies + DoCast(m_creature,6924); //visual explosion + } + } + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //TODO: add abilities for the different creatures + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_mobs_spitelashes(Creature *_Creature) +{ + return new mobs_spitelashesAI (_Creature); +} + +/*###### +## npc_loramus_thalipedes +######*/ + +bool GossipHello_npc_loramus_thalipedes(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(2744) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Can you help me?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + if (player->GetQuestStatus(3141) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Tell me your story", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_loramus_thalipedes(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(2744); + break; + + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "Please continue", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 21); + player->SEND_GOSSIP_MENU(1813, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+21: + player->ADD_GOSSIP_ITEM( 0, "I do not understand", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 22); + player->SEND_GOSSIP_MENU(1814, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+22: + player->ADD_GOSSIP_ITEM( 0, "Indeed", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 23); + player->SEND_GOSSIP_MENU(1815, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+23: + player->ADD_GOSSIP_ITEM( 0, "I will do this with or your help, Loramus", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 24); + player->SEND_GOSSIP_MENU(1816, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+24: + player->ADD_GOSSIP_ITEM( 0, "Yes", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 25); + player->SEND_GOSSIP_MENU(1817, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+25: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(3141); + break; + } + return true; +} + +void AddSC_azshara() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mobs_spitelashes"; + newscript->GetAI = GetAI_mobs_spitelashes; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_loramus_thalipedes"; + newscript->pGossipHello = &GossipHello_npc_loramus_thalipedes; + newscript->pGossipSelect = &GossipSelect_npc_loramus_thalipedes; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/azshara/boss_azuregos.cpp b/src/bindings/scripts/scripts/zone/azshara/boss_azuregos.cpp new file mode 100644 index 00000000000..5193fd841a1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/azshara/boss_azuregos.cpp @@ -0,0 +1,153 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Azuregos +SD%Complete: 90 +SDComment: Teleport not included, spell reflect not effecting dots (Core problem) +SDCategory: Azshara +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_MARKOFFROST 23182 +#define SPELL_MANASTORM 21097 +#define SPELL_CHILL 21098 +#define SPELL_FROSTBREATH 21099 +#define SPELL_REFLECT 22067 //Old one was 30969 +#define SPELL_CLEAVE 8255 //Perhaps not right ID +#define SPELL_ENRAGE 23537 + +struct MANGOS_DLL_DECL boss_azuregosAI : public ScriptedAI +{ + boss_azuregosAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 MarkOfFrost_Timer; + uint32 ManaStorm_Timer; + uint32 Chill_Timer; + uint32 Breath_Timer; + uint32 Teleport_Timer; + uint32 Reflect_Timer; + uint32 Cleave_Timer; + uint32 Enrage_Timer; + bool Enraged; + + void Reset() + { + MarkOfFrost_Timer = 35000; + ManaStorm_Timer = 5000 + rand()%12000; + Chill_Timer = 10000 + rand()%20000; + Breath_Timer = 2000 + rand()%6000; + Teleport_Timer = 30000; + Reflect_Timer = 15000 + rand()%15000; + Cleave_Timer = 7000; + Enrage_Timer = 0; + Enraged = false; + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(Teleport_Timer < diff) + { + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + std::list::iterator i = m_threatlist.begin(); + for (i = m_threatlist.begin(); i!= m_threatlist.end();++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit && (pUnit->GetTypeId() == TYPEID_PLAYER)) + { + DoTeleportPlayer(pUnit, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()+3, pUnit->GetOrientation()); + } + } + + DoResetThreat(); + Teleport_Timer = 30000; + }else Teleport_Timer -= diff; + + // //MarkOfFrost_Timer + // if (MarkOfFrost_Timer < diff) + // { + // DoCast(m_creature->getVictim(),SPELL_MARKOFFROST); + // MarkOfFrost_Timer = 25000; + // }else MarkOfFrost_Timer -= diff; + + //Chill_Timer + if (Chill_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CHILL); + Chill_Timer = 13000 + rand()%12000; + }else Chill_Timer -= diff; + + //Breath_Timer + if (Breath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROSTBREATH); + Breath_Timer = 10000 + rand()%5000; + }else Breath_Timer -= diff; + + //ManaStorm_Timer + if (ManaStorm_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + DoCast(target,SPELL_MANASTORM); + ManaStorm_Timer = 7500 + rand()%5000; + }else ManaStorm_Timer -= diff; + + //Reflect_Timer + if (Reflect_Timer < diff) + { + DoCast(m_creature,SPELL_REFLECT); + Reflect_Timer = 20000 + rand()%15000; + }else Reflect_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 7000; + }else Cleave_Timer -= diff; + + //Enrage_Timer + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 26 && !Enraged) + { + DoCast(m_creature, SPELL_ENRAGE); + Enraged = true; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_azuregos(Creature *_Creature) +{ + return new boss_azuregosAI (_Creature); +} + +void AddSC_boss_azuregos() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_azuregos"; + newscript->GetAI = GetAI_boss_azuregos; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/azuremyst_isle/azuremyst_isle.cpp b/src/bindings/scripts/scripts/zone/azuremyst_isle/azuremyst_isle.cpp new file mode 100644 index 00000000000..f1f68ff4ee3 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/azuremyst_isle/azuremyst_isle.cpp @@ -0,0 +1,372 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Azuremyst_Isle +SD%Complete: 75 +SDComment: Quest support: 9283, 9537, 9554(special flight path, proper model for mount missing). Injured Draenei cosmetic only +SDCategory: Azuremyst Isle +EndScriptData */ + +/* ContentData +npc_draenei_survivor +npc_engineer_spark_overgrind +npc_injured_draenei +npc_susurrus +EndContentData */ + +#include "precompiled.h" +#include + +/*###### +## npc_draenei_survivor +######*/ + +#define HEAL1 "The last thing I remember is the ship falling and us getting into the pods. I'll go see how I can help. Thank you!" +#define HEAL2 "$C, Where am I? Who are you? Oh no! What happened to the ship?." +#define HEAL3 "$C You saved me! I owe you a debt that I can never repay. I'll go see if I can help the others." +#define HEAL4 "Ugh... what is this place? Is that all that's left of the ship over there?" + +#define HELP1 "Oh, the pain..." +#define HELP2 "Everything hurts, Please make it stop..." +#define HELP3 "Ughhh... I hurt. Can you help me?" +#define HELP4 "I don't know if I can make it, please help me..." + +struct MANGOS_DLL_DECL npc_draenei_survivorAI : public ScriptedAI +{ + npc_draenei_survivorAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 UnSpawnTimer; + uint32 ResetlifeTimer; + uint32 SayingTimer; + uint32 HealSayTimer; + bool UnSpawn; + bool say; + bool HealSay; + bool isRun; + bool isMove; + + void Reset() + { + UnSpawnTimer = 2500; + ResetlifeTimer= 60000; + SayingTimer = 5000; + HealSayTimer = 6000; + say = false; + isRun = false; + isMove = false; + UnSpawn = false; + HealSay = false; + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + //cast red shining + m_creature->CastSpell(m_creature, 29152, false, NULL); + //set creature health + m_creature->SetHealth(int(m_creature->GetMaxHealth()*.1)); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 3); + } + + void Aggro(Unit *who) {} + + void MoveInLineOfSight(Unit *who) //MoveInLineOfSight is called if creature could see you, updated all 100 ms + { + if (!who) + return; + + if(who->GetTypeId() == TYPEID_PLAYER && m_creature->IsFriendlyTo(who) && m_creature->IsWithinDistInMap(who, 15) && say && !isRun) + { + switch (rand()%4) //Random switch between 4 texts + { + case 0: + DoSay(HELP1, LANG_UNIVERSAL, NULL); + SayingTimer = 15000; + say = false; + break; + case 1: + DoSay(HELP2, LANG_UNIVERSAL, NULL); + SayingTimer = 15000; + say = false; + break; + case 2: + DoSay(HELP3, LANG_UNIVERSAL, NULL); + SayingTimer = 15000; + say = false; + break; + case 3: + DoSay(HELP4, LANG_UNIVERSAL, NULL); + SayingTimer = 15000; + say = false; + break; + } + } + else + { + isRun = false; + } + } + + void UpdateAI(const uint32 diff) //Is also called each ms for Creature AI Updates... + { + if (m_creature->GetHealth() > 50) + { + if(ResetlifeTimer < diff) + { + ResetlifeTimer = 60000; + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + //set creature health + m_creature->SetHealth(int(m_creature->GetMaxHealth()*.1)); + // ley down + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,3); + } + else ResetlifeTimer -= diff; + } + + if(HealSay) + { + if (HealSayTimer < diff) + { + UnSpawn = true; + isRun = true; + isMove = true; + }else HealSayTimer -= diff; + } + + if(UnSpawn) + { + if(isMove) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(0, -4115.053711f, -13754.831055f, 73.508949f); + isMove = false; + } + + if (UnSpawnTimer < diff) + { + m_creature->StopMoving(); + EnterEvadeMode(); + //set creature health + m_creature->SetHealth(int(m_creature->GetMaxHealth()*.1)); + + }else UnSpawnTimer -= diff; + } + + if(SayingTimer < diff) + { + say = true; + }else SayingTimer -= diff; + } + + void SpellHit(Unit *Hitter, const SpellEntry *Spellkind)//Called if you cast a spell and do some things if Specified spell is true! + { + if (Hitter && Spellkind->Id == 28880) + { + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + m_creature->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED); + m_creature->HandleEmoteCommand(ANIM_RISE); + switch (rand()%4) //This switch doesn't work at all, creature say nothing! + { + case 0: DoSay(HEAL1, LANG_UNIVERSAL, Hitter); break; + case 1: DoSay(HEAL2, LANG_UNIVERSAL, Hitter); break; + case 2: DoSay(HEAL3, LANG_UNIVERSAL, Hitter); break; + case 3: DoSay(HEAL4, LANG_UNIVERSAL, Hitter); break; + } + HealSay = true; + } + } +}; +CreatureAI* GetAI_npc_draenei_survivor(Creature *_Creature) +{ + return new npc_draenei_survivorAI (_Creature); +} + +/*###### +## npc_engineer_spark_overgrind +######*/ + +#define SAY_TEXT "Yes Master, all goes along as planned." +#define SAY_EMOTE "puts the shell to his ear." +#define GOSSIP_FIGHT "Traitor! You will be brought to justice!" +#define ATTACK_YELL "Now I cut you!" +#define SPELL_DYNAMITE 7978 + +struct MANGOS_DLL_DECL npc_engineer_spark_overgrindAI : public ScriptedAI +{ + npc_engineer_spark_overgrindAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Dynamite_Timer; + uint32 Emote_Timer; + + void Reset() + { + Dynamite_Timer = 8000; + Emote_Timer = 120000 + rand()%30000; + m_creature->setFaction(875); + } + + void Aggro(Unit *who) { } + + void UpdateAI(const uint32 diff) + { + if( !InCombat ) + { + if (Emote_Timer < diff) + { + DoSay(SAY_TEXT,LANG_UNIVERSAL,NULL); + DoTextEmote(SAY_EMOTE,NULL); + Emote_Timer = 120000 + rand()%30000; + }else Emote_Timer -= diff; + } + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (Dynamite_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_DYNAMITE); + Dynamite_Timer = 8000; + } else Dynamite_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_engineer_spark_overgrind(Creature *_Creature) +{ + return new npc_engineer_spark_overgrindAI (_Creature); +} + +bool GossipHello_npc_engineer_spark_overgrind(Player *player, Creature *_Creature) +{ + if( player->GetQuestStatus(9537) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM(0, GOSSIP_FIGHT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_engineer_spark_overgrind(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_ACTION_INFO_DEF ) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->setFaction(14); + _Creature->Yell(ATTACK_YELL, LANG_UNIVERSAL, player->GetGUID()); + ((npc_engineer_spark_overgrindAI*)_Creature->AI())->AttackStart(player); + } + return true; +} + +/*###### +## npc_injured_draenei +######*/ + +struct MANGOS_DLL_DECL npc_injured_draeneiAI : public ScriptedAI +{ + npc_injured_draeneiAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + m_creature->SetHealth(int(m_creature->GetMaxHealth()*.15)); + switch (rand()%2) + { + case 0: m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 1); break; + case 1: m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 3); break; + } + } + + void Aggro(Unit *who) {} + + void MoveInLineOfSight(Unit *who) + { + return; //ignore everyone around them (won't aggro anything) + } + + void UpdateAI(const uint32 diff) + { + return; + } + +}; +CreatureAI* GetAI_npc_injured_draenei(Creature *_Creature) +{ + return new npc_injured_draeneiAI (_Creature); +} + +/*###### +## npc_susurrus +######*/ + +bool GossipHello_npc_susurrus(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->HasItemCount(23843,1,true)) + player->ADD_GOSSIP_ITEM(0, "I am ready.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_susurrus(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,32474,true); //apparently correct spell, possible not correct place to cast, or correct caster + + std::vector nodes; + + nodes.resize(2); + nodes[0] = 92; //from susurrus + nodes[1] = 91; //end at exodar + player->ActivateTaxiPathTo(nodes,11686); //TaxiPath 506. Using invisible model, possible mangos must allow 0(from dbc) for cases like this. + } + return true; +} + +/*###### +## +######*/ + +void AddSC_azuremyst_isle() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_draenei_survivor"; + newscript->GetAI = GetAI_npc_draenei_survivor; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_engineer_spark_overgrind"; + newscript->GetAI = GetAI_npc_engineer_spark_overgrind; + newscript->pGossipHello = &GossipHello_npc_engineer_spark_overgrind; + newscript->pGossipSelect = &GossipSelect_npc_engineer_spark_overgrind; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_injured_draenei"; + newscript->GetAI = GetAI_npc_injured_draenei; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_susurrus"; + newscript->pGossipHello = &GossipHello_npc_susurrus; + newscript->pGossipSelect = &GossipSelect_npc_susurrus; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/barrens/the_barrens.cpp b/src/bindings/scripts/scripts/zone/barrens/the_barrens.cpp new file mode 100644 index 00000000000..b09dd9222de --- /dev/null +++ b/src/bindings/scripts/scripts/zone/barrens/the_barrens.cpp @@ -0,0 +1,189 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: The_Barrens +SD%Complete: 90 +SDComment: Quest support: 2458, 4921, 6981 +SDCategory: Barrens +EndScriptData */ + +/* ContentData +npc_beaten_corpse +npc_sputtervalve +npc_taskmaster_fizzule remove hack when mangos implement feature/detect spell kind to not aggro +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_beaten_corpse +######*/ + +bool GossipHello_npc_beaten_corpse(Player *player, Creature *_Creature) +{ + if( player->GetQuestStatus(4921) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(4921) == QUEST_STATUS_COMPLETE) + player->ADD_GOSSIP_ITEM(0,"Examine corpse in detail...",GOSSIP_SENDER_MAIN,GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(3557,_Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_beaten_corpse(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if(action == GOSSIP_ACTION_INFO_DEF +1) + { + player->SEND_GOSSIP_MENU(3558,_Creature->GetGUID()); + player->KilledMonster( 10668,_Creature->GetGUID() ); + } + return true; +} + +/*###### +## npc_sputtervalve +######*/ + +bool GossipHello_npc_sputtervalve(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( player->GetQuestStatus(6981) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM(0,"Can you tell me about this shard?",GOSSIP_SENDER_MAIN,GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(),_Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_sputtervalve(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if(action == GOSSIP_ACTION_INFO_DEF) + { + player->SEND_GOSSIP_MENU(2013,_Creature->GetGUID()); + player->AreaExploredOrEventHappens(6981); + } + return true; +} + +/*###### +## npc_taskmaster_fizzule +######*/ + +//#define FACTION_HOSTILE_F 430 +#define FACTION_HOSTILE_F 16 +#define FACTION_FRIENDLY_F 35 + +#define SPELL_FLARE 10113 +#define SPELL_FOLLY 10137 + +struct MANGOS_DLL_DECL npc_taskmaster_fizzuleAI : public ScriptedAI +{ + npc_taskmaster_fizzuleAI(Creature* c) : ScriptedAI(c) { Reset(); } + + bool IsFriend; + uint32 Reset_Timer; + uint32 FlareCount; + + void Reset() + { + IsFriend = false; + Reset_Timer = 120000; + FlareCount = 0; + m_creature->setFaction(FACTION_HOSTILE_F); + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + } + + //This is a hack. Spellcast will make creature aggro but that is not + //supposed to happen (mangos not implemented/not found way to detect this spell kind) + void DoUglyHack() + { + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(); + } + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + if( spell->Id == SPELL_FLARE || spell->Id == SPELL_FOLLY ) + { + DoUglyHack(); + ++FlareCount; + if( FlareCount >= 2 ) + { + m_creature->setFaction(FACTION_FRIENDLY_F); + IsFriend = true; + } + } + } + + void Aggro(Unit* who) { } + + void UpdateAI(const uint32 diff) + { + if( IsFriend ) + { + if( Reset_Timer < diff ) + { + EnterEvadeMode(); + } else Reset_Timer -= diff; + } + + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_taskmaster_fizzule(Creature *_Creature) +{ + return new npc_taskmaster_fizzuleAI (_Creature); +} + +bool ReciveEmote_npc_taskmaster_fizzule(Player *player, Creature *_Creature, uint32 emote) +{ + if( emote == TEXTEMOTE_SALUTE ) + { + if( ((npc_taskmaster_fizzuleAI*)_Creature->AI())->FlareCount >= 2 ) + { + _Creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER); + _Creature->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + } + } + return true; +} + +void AddSC_the_barrens() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_beaten_corpse"; + newscript->pGossipHello = &GossipHello_npc_beaten_corpse; + newscript->pGossipSelect = &GossipSelect_npc_beaten_corpse; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_sputtervalve"; + newscript->pGossipHello = &GossipHello_npc_sputtervalve; + newscript->pGossipSelect = &GossipSelect_npc_sputtervalve; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_taskmaster_fizzule"; + newscript->GetAI = GetAI_npc_taskmaster_fizzule; + newscript->pReceiveEmote = &ReciveEmote_npc_taskmaster_fizzule; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/black_temple.cpp b/src/bindings/scripts/scripts/zone/black_temple/black_temple.cpp new file mode 100644 index 00000000000..137d87ed8a6 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/black_temple.cpp @@ -0,0 +1,68 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Black_Temple +SD%Complete: 95 +SDComment: Spirit of Olum: Player Teleporter to Seer Kanai Teleport after defeating Naj'entus and Supremus. TODO: Find proper gossip. +SDCategory: Black Temple +EndScriptData */ + +/* ContentData +npc_spirit_of_olum +EndContentData */ + +#include "precompiled.h" +#include "def_black_temple.h" + +/*### +# npc_spirit_of_olum +####*/ + +#define SPELL_TELEPORT 41566 // s41566 - Teleport to Ashtongue NPC's +#define GOSSIP_OLUM1 "Teleport me to the other Ashtongue Deathsworn" + +bool GossipHello_npc_spirit_of_olum(Player* player, Creature* _Creature) +{ + ScriptedInstance* pInstance = ((ScriptedInstance*)_Creature->GetInstanceData()); + + if(pInstance && (pInstance->GetData(DATA_SUPREMUSEVENT) >= DONE) && (pInstance->GetData(DATA_HIGHWARLORDNAJENTUSEVENT) >= DONE)) + player->ADD_GOSSIP_ITEM(0, GOSSIP_OLUM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_spirit_of_olum(Player* player, Creature* _Creature, uint32 sender, uint32 action) +{ + if(action == GOSSIP_ACTION_INFO_DEF + 1) + player->CLOSE_GOSSIP_MENU(); + + player->InterruptNonMeleeSpells(false); + player->CastSpell(player, SPELL_TELEPORT, false); + return true; +} + +void AddSC_black_temple() +{ + Script *newscript; + + newscript = new Script; + newscript->Name = "npc_spirit_of_olum"; + newscript->pGossipHello = GossipHello_npc_spirit_of_olum; + newscript->pGossipSelect = GossipSelect_npc_spirit_of_olum; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/boss_bloodboil.cpp b/src/bindings/scripts/scripts/zone/black_temple/boss_bloodboil.cpp new file mode 100644 index 00000000000..fba3f7fc36d --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/boss_bloodboil.cpp @@ -0,0 +1,365 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Bloodboil +SD%Complete: 80 +SDComment: Bloodboil not working correctly, missing enrage +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "def_black_temple.h" + +//Spells +#define SPELL_ACID_GEYSER 40630 +#define SPELL_ACIDIC_WOUND 40481 +#define SPELL_ARCING_SMASH 40599 +#define SPELL_BLOODBOIL 42005 // This spell is AoE whereas it shouldn't be +#define SPELL_FEL_ACID 40508 +#define SPELL_FEL_RAGE_SELF 40594 +#define SPELL_FEL_RAGE_TARGET 40604 +#define SPELL_FEL_RAGE_2 40616 +#define SPELL_FEL_RAGE_3 41625 +#define SPELL_BEWILDERING_STRIKE 40491 +#define SPELL_EJECT1 40486 // 1000 Physical damage + knockback + script effect (should handle threat reduction I think) +#define SPELL_EJECT2 40597 // 1000 Physical damage + Stun (used in phase 2?) +#define SPELL_TAUNT_GURTOGG 40603 +#define SPELL_INSIGNIFIGANCE 40618 +#define SPELL_BERSERK 45078 + +//Speech'n'Sound +#define SAY_AGGRO "Horde will crush you!" +#define SOUND_AGGRO 11432 + +#define SAY_SLAY1 "Time to feast!" +#define SOUND_SLAY1 11433 + +#define SAY_SLAY2 "More! I want more!" +#define SOUND_SLAY2 11434 + +#define SAY_SPECIAL1 "Drink your blood! Eat your flesh!" +#define SOUND_SPECIAL1 11435 + +#define SAY_SPECIAL2 "I hunger!" +#define SOUND_SPECIAL2 11436 + +#define SAY_ENRAGE "I'll rip the meat from your bones!" +#define SOUND_ENRAGE 11437 + +#define SOUND_DEATH 11439 + +//This is used to sort the players by distance in preparation for the Bloodboil cast. +struct TargetDistanceOrder : public std::binary_function +{ + 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)); + } +}; + +struct MANGOS_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI +{ + boss_gurtogg_bloodboilAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 TargetGUID; + + float TargetThreat; + + uint32 BloodboilTimer; + uint32 BloodboilCount; + uint32 AcidGeyserTimer; + uint32 AcidicWoundTimer; + uint32 ArcingSmashTimer; + uint32 EnrageTimer; + uint32 FelAcidTimer; + uint32 EjectTimer; + uint32 BewilderingStrikeTimer; + uint32 PhaseChangeTimer; + + bool Phase1; + + void Reset() + { + if(pInstance) + pInstance->SetData(DATA_GURTOGGBLOODBOILEVENT, NOT_STARTED); + + TargetGUID = 0; + + TargetThreat = 0; + + BloodboilTimer = 10000; + BloodboilCount = 0; + AcidGeyserTimer = 1000; + AcidicWoundTimer = 6000; + ArcingSmashTimer = 19000; + EnrageTimer = 600000; + FelAcidTimer = 25000; + EjectTimer = 10000; + BewilderingStrikeTimer = 15000; + PhaseChangeTimer = 60000; + + Phase1 = true; + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + if(pInstance) + pInstance->SetData(DATA_GURTOGGBLOODBOILEVENT, IN_PROGRESS); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *victim) + { + if(pInstance) + pInstance->SetData(DATA_GURTOGGBLOODBOILEVENT, DONE); + + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + // Note: This seems like a very complicated fix. The fix needs to be handled by the core, as implementation of limited-target AoE spells are still not limited. + void CastBloodboil() + { + // Get the Threat List + std::list m_threatlist = m_creature->getThreatManager().getThreatList(); + + if(!m_threatlist.size()) return; // He doesn't have anyone in his threatlist, useless to continue + + std::list targets; + std::list::iterator itr = m_threatlist.begin(); + for( ; itr!= m_threatlist.end(); ++itr) //store the threat list in a different container + { + Unit *target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + //only on alive players + if(target && target->isAlive() && target->GetTypeId() == TYPEID_PLAYER ) + targets.push_back( target); + } + + //Sort the list of players + targets.sort(TargetDistanceOrder(m_creature)); + //Resize so we only get top 5 + targets.resize(5); + + //Aura each player in the targets list with Bloodboil. Aura code copied+pasted from Aura command in Level3.cpp + /*SpellEntry const *spellInfo = GetSpellStore()->LookupEntry( SPELL_BLOODBOIL ); + if(spellInfo) + { + for(std::list::iterator itr = targets.begin(); itr != targets.end(); ++itr) + { + Unit* target = *itr; + if(!target) return; + for(uint32 i = 0;i<3;i++) + { + uint8 eff = spellInfo->Effect[i]; + if (eff>=TOTAL_SPELL_EFFECTS) + continue; + + Aura *Aur = new Aura(spellInfo, i, NULL, target); + target->AddAura(Aur); + } + } + }*/ + } + + void RevertThreatOnTarget(uint64 guid) + { + Unit* pUnit = NULL; + pUnit = Unit::GetUnit((*m_creature), guid); + if(pUnit) + { + if(m_creature->getThreatManager().getThreat(pUnit)) + m_creature->getThreatManager().modifyThreatPercent(pUnit, -100); + if(TargetThreat) + m_creature->AddThreat(pUnit, TargetThreat); + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(ArcingSmashTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ARCING_SMASH); + ArcingSmashTimer = 10000; + }else ArcingSmashTimer -= diff; + + if(FelAcidTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FEL_ACID); + FelAcidTimer = 25000; + }else FelAcidTimer -= diff; + + if(!m_creature->HasAura(SPELL_BERSERK, 0)) + if(EnrageTimer < diff) + { + DoCast(m_creature, SPELL_BERSERK); + DoYell(SAY_ENRAGE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + }else EnrageTimer -= diff; + + if(Phase1) + { + if(BewilderingStrikeTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_BEWILDERING_STRIKE); + float mt_threat = m_creature->getThreatManager().getThreat(m_creature->getVictim()); + Unit* target = SelectUnit(SELECT_TARGET_TOPAGGRO, 1); + m_creature->AddThreat(target, mt_threat); + BewilderingStrikeTimer = 20000; + }else BewilderingStrikeTimer -= diff; + + if(EjectTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_EJECT1); + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(), -40); + EjectTimer = 15000; + }else EjectTimer -= diff; + + if(AcidicWoundTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ACIDIC_WOUND); + AcidicWoundTimer = 10000; + }else AcidicWoundTimer -= diff; + + if(BloodboilTimer < diff) + { + if(BloodboilCount < 5) // Only cast it five times. + { + //CastBloodboil(); // Causes issues on windows, so is commented out. + DoCast(m_creature->getVictim(), SPELL_BLOODBOIL); + ++BloodboilCount; + BloodboilTimer = 10000*BloodboilCount; + } + }else BloodboilTimer -= diff; + } + + if(!Phase1) + { + if(AcidGeyserTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ACID_GEYSER); + AcidGeyserTimer = 30000; + }else AcidGeyserTimer -= diff; + + if(EjectTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_EJECT2); + EjectTimer = 15000; + }else EjectTimer -= diff; + } + + if(PhaseChangeTimer < diff) + { + if(Phase1) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target && target->isAlive()) + { + Phase1 = false; + + TargetThreat = m_creature->getThreatManager().getThreat(target); + TargetGUID = target->GetGUID(); + target->CastSpell(m_creature, SPELL_TAUNT_GURTOGG, true); + if(m_creature->getThreatManager().getThreat(target)) + m_creature->getThreatManager().modifyThreatPercent(target, -100); + m_creature->AddThreat(target, 50000000.0f); + // If VMaps are disabled, this spell can call the whole instance + DoCast(m_creature, SPELL_INSIGNIFIGANCE, true); + DoCast(target, SPELL_FEL_RAGE_TARGET, true); + DoCast(target,SPELL_FEL_RAGE_2, true); + /* These spells do not work, comment them out for now. + DoCast(target, SPELL_FEL_RAGE_2, true); + DoCast(target, SPELL_FEL_RAGE_3, true);*/ + + //Cast this without triggered so that it appears in combat logs and shows visual. + DoCast(m_creature, SPELL_FEL_RAGE_SELF); + + switch(rand()%2) + { + case 0: + DoYell(SAY_SPECIAL1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SPECIAL1); + break; + case 1: + DoYell(SAY_SPECIAL2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SPECIAL2); + break; + } + + AcidGeyserTimer = 1000; + PhaseChangeTimer = 30000; + } + }else // Encounter is a loop pretty much. Phase 1 -> Phase 2 -> Phase 1 -> Phase 2 till death or enrage + { + if(TargetGUID) + RevertThreatOnTarget(TargetGUID); + TargetGUID = 0; + Phase1 = true; + BloodboilTimer = 10000; + BloodboilCount = 0; + AcidicWoundTimer += 2000; + ArcingSmashTimer += 2000; + FelAcidTimer += 2000; + EjectTimer += 2000; + PhaseChangeTimer = 60000; + } + }else PhaseChangeTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_gurtogg_bloodboil(Creature *_Creature) +{ + return new boss_gurtogg_bloodboilAI (_Creature); +} + +void AddSC_boss_gurtogg_bloodboil() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_gurtogg_bloodboil"; + newscript->GetAI = GetAI_boss_gurtogg_bloodboil; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/boss_illidan.cpp b/src/bindings/scripts/scripts/zone/black_temple/boss_illidan.cpp new file mode 100644 index 00000000000..ecf8ad427be --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/boss_illidan.cpp @@ -0,0 +1,2465 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: boss_illidan_stormrage +SD%Complete: 90 +SDComment: +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "def_black_temple.h" +#include "WorldPacket.h" + +/************* Quotes and Sounds ***********************/ +// Gossip for when a player clicks Akama +#define GOSSIP_ITEM "We are ready to face Illidan" + +// Yells for/by Akama +#define SAY_AKAMA_BEWARE "Be wary friends, The Betrayer meditates in the court just beyond." +#define SOUND_AKAMA_BEWARE 11388 +#define SAY_AKAMA_MINION "Come, my minions. Deal with this traitor as he deserves!" +#define SOUND_AKAMA_MINION 11465 +#define SAY_AKAMA_LEAVE "I'll deal with these mongrels. Strike now, friends! Strike at the betrayer!" +#define SOUND_AKAMA_LEAVE 11390 + +// Self explanatory +#define SAY_KILL1 "Who shall be next to taste my blades?!" +#define SOUND_KILL1 11473 +#define SAY_KILL2 "This is too easy!" +#define SOUND_KILL2 11472 + +// I think I'll fly now and let my subordinates take you on +#define SAY_TAKEOFF "I will not be touched by rabble such as you!" +#define SOUND_TAKEOFF 11479 +#define SAY_SUMMONFLAMES "Behold the flames of Azzinoth!" +#define SOUND_SUMMONFLAMES 11480 + +// When casting Eye Blast. Demon Fire will be appear on places that he casts this +#define SAY_EYE_BLAST "Stare into the eyes of the Betrayer!" +#define SOUND_EYE_BLAST 11481 + +// kk, I go big, dark and demon on you. +#define SAY_MORPH "Behold the power... of the demon within!" +#define SOUND_MORPH 11475 + +// I KILL! +#define SAY_ENRAGE "You've wasted too much time mortals, now you shall fall!" +#define SOUND_ENRAGE 11474 + +/************** Spells *************/ +// Normal Form +#define SPELL_SHEAR 41032 // Reduces Max. Health by 60% for 7 seconds. Can stack 19 times. 1.5 second cast +#define SPELL_FLAME_CRASH 40832 // Summons an invis/unselect passive mob that has an aura of flame in a circle around him. +#define SPELL_DRAW_SOUL 40904 // 5k Shadow Damage in front of him. Heals Illidan for 100k health (script effect) +#define SPELL_PARASITIC_SHADOWFIEND 41917 // DoT of 3k Shadow every 2 seconds. Lasts 10 seconds. (Script effect: Summon 2 parasites once the debuff has ticked off) +#define SPELL_SUMMON_PARASITICS 41915 // Summons 2 Parasitic Shadowfiends on the target. It's supposed to be cast as soon as the Parasitic Shadowfiend debuff is gone, but the spells aren't linked :( +#define SPELL_AGONIZING_FLAMES 40932 // 4k fire damage initial to target and anyone w/i 5 yards. PHASE 3 ONLY +#define SPELL_ENRAGE 40683 // Increases damage by 50% and attack speed by 30%. 20 seconds, PHASE 5 ONLY +// Flying (Phase 2) +#define SPELL_THROW_GLAIVE 39635 // Throws a glaive on the ground +#define SPELL_THROW_GLAIVE2 39849 // Animation for the above spell +#define SPELL_GLAIVE_RETURNS 39873 // Glaive flies back to Illidan +#define SPELL_FIREBALL 40598 // 2.5k-3.5k damage in 10 yard radius. 2 second cast time. +#define SPELL_DARK_BARRAGE 40585 // 10 second channeled spell, 3k shadow damage per second. +// Demon Form +#define SPELL_DEMON_TRANSFORM_1 40511 // First phase of animations for transforming into Dark Illidan (fall to ground) +#define SPELL_DEMON_TRANSFORM_2 40398 // Second phase of animations (kneel) +#define SPELL_DEMON_TRANSFORM_3 40510 // Final phase of animations (stand up and roar) +#define SPELL_DEMON_FORM 40506 // Transforms into Demon Illidan. Has an Aura of Dread on him. +#define SPELL_SHADOW_BLAST 41078 // 8k - 11k Shadow Damage. Targets highest threat. Has a splash effect, damaging anyone in 20 yards of the target. +#define SPELL_FLAME_BURST 41126 // Hurls fire at entire raid for ~3.5k damage every 10 seconds. Resistable. (Does not work: Script effect) +#define SPELL_FLAME_BURST_EFFECT 41131 // The actual damage. Handled by core (41126 triggers 41131) +// Other Illidan spells +#define SPELL_KNEEL 39656 // Before beginning encounter, this is how he appears (talking to Wilson). +#define SPELL_SHADOW_PRISON 40647 // Illidan casts this spell to immobilize entire raid when he summons Maiev. +#define SPELL_DEATH 41220 // This spell doesn't do anything except stun Illidan and set him on his knees. +#define SPELL_BERSERK 45078 // Damage increased by 500%, attack speed by 150% + +// Non-Illidan spells +#define SPELL_AKAMA_DOOR_CHANNEL 41268 // Akama's channel spell on the door before the Temple Summit +#define SPELL_DEATHSWORN_DOOR_CHANNEL 41269 // Olum and Udalo's channel spell on the door before the Temple Summit +#define SPELL_AKAMA_DOOR_FAIL 41271 // Not sure where this is really used... +#define SPELL_HEALING_POTION 40535 // Akama uses this to heal himself to full. +#define SPELL_AZZINOTH_CHANNEL 39857 // Glaives cast it on Flames. Not sure if this is the right spell. +#define SPELL_SHADOW_DEMON_PASSIVE 41079 // Adds the "shadowform" aura to Shadow Demons. +#define SPELL_CONSUME_SOUL 41080 // Once the Shadow Demons reach their target, they use this to kill them +#define SPELL_PARALYZE 41083 // Shadow Demons cast this on their target +#define SPELL_PURPLE_BEAM 39123 // Purple Beam connecting Shadow Demon to their target +#define SPELL_CAGE_TRAP_DUMMY 40761 // Put this in DB for cage trap GO. +#define SPELL_EYE_BLAST_TRIGGER 40017 // This summons Demon Form every few seconds and deals ~20k damage in its radius +#define SPELL_EYE_BLAST 39908 // This does the blue flamey animation. +#define SPELL_FLAME_CRASH_EFFECT 40836 // Firey blue ring of circle that the other flame crash summons +#define SPELL_BLAZE_EFFECT 40610 // Green flame on the ground, triggers damage (5k) every few seconds +#define SPELL_BLAZE_SUMMON 40637 // Summons the Blaze creature +#define SPELL_DEMON_FIRE 40029 // Blue fire trail left by Eye Blast. Deals 2k per second if players stand on it. +#define SPELL_CAGED 40695 // Caged Trap triggers will cast this on Illidan if he is within 3 yards +#define SPELL_CAGE_TRAP_SUMMON 40694 // Summons a Cage Trap GO (bugged) on the ground along with a Cage Trap Disturb Trigger mob (working) +#define SPELL_CAGE_TRAP_BEAM 40713 // 8 Triggers on the ground in an octagon cast spells like this on Illidan 'caging him' +#define SPELL_FLAME_BLAST 40631 // Flames of Azzinoth use this. Frontal cone AoE 7k-9k damage. +#define SPELL_CHARGE 40602 // Flames of Azzinoth charges whoever is too far from them. They enrage after this. For simplicity, we'll use the same enrage as Illidan. +#define SPELL_TELEPORT_VISUAL 41232 // Teleport visual for Maiev +#define SPELL_SHADOWFIEND_PASSIVE 41913 // Passive aura for shadowfiends + +// Other defines +#define CENTER_X 676.740 +#define CENTER_Y 305.297 +#define CENTER_Z 353.192 + +/**** Creature Summon and Recognition IDs ****/ +enum CreatureEntry +{ + EMPTY = 0, + AKAMA = 22990, + ILLIDAN_STORMRAGE = 22917, + BLADE_OF_AZZINOTH = 22996, + FLAME_OF_AZZINOTH = 22997, + MAIEV_SHADOWSONG = 23197, + SHADOW_DEMON = 23375, + DEMON_FIRE = 23069, + FLAME_CRASH = 23336, + ILLIDAN_DOOR_TRIGGER = 23412, + SPIRIT_OF_OLUM = 23411, + SPIRIT_OF_UDALO = 23410, + ILLIDARI_ELITE = 23226, + PARASITIC_SHADOWFIEND = 23498, + CAGE_TRAP_TRIGGER = 23292, +}; + +/*** Phase Names ***/ +enum Phase +{ + PHASE_NORMAL = 1, + PHASE_FLIGHT = 2, + PHASE_NORMAL_2 = 3, + PHASE_DEMON = 4, + PHASE_NORMAL_MAIEV = 5, + PHASE_DEMON_SEQUENCE = 6, +}; + +struct Yells +{ + uint32 sound; + char* text; + uint32 creature, timer, emote; + bool Talk; +}; + +static Yells Conversation[]= +{ + {11463, "Akama... your duplicity is hardly surprising. I should have slaughtered you and your malformed brethren long ago.", ILLIDAN_STORMRAGE, 8000, 0, true}, + {0, NULL, ILLIDAN_STORMRAGE, 5000, 396, true}, + {11389, "We've come to end your reign, Illidan. My people and all of Outland shall be free!", AKAMA, 7000, 25, true}, + {0, NULL, AKAMA, 5000, 66, true}, + {11464, "Boldly said. But I remain unconvinced.", ILLIDAN_STORMRAGE, 8000, 396, true}, + {11380, "The time has come! The moment is at hand!", AKAMA, 3000, 22, true}, + {0, NULL, AKAMA, 2000, 15, true}, + {11466, "You are not prepared!", ILLIDAN_STORMRAGE, 3000, 406, true}, + {0, NULL, EMPTY, 1000, 0, true}, + {0, NULL, EMPTY, 0, 0, false}, + {11476, "Is this it, mortals? Is this all the fury you can muster?", ILLIDAN_STORMRAGE, 8000, 0, true}, + {11491, "Their fury pales before mine, Illidan. We have some unsettled business between us.", MAIEV_SHADOWSONG, 8000, 5, true}, + {11477, "Maiev... How is this even possible?", ILLIDAN_STORMRAGE, 7000, 1, true}, + {11492, "Ah... my long hunt is finally over. Today, Justice will be done!", MAIEV_SHADOWSONG, 8000, 15, true}, + {11470, "Feel the hatred of ten thousand years!", ILLIDAN_STORMRAGE, 1000, 0, false}, + {11496, "Ahh... It is finished. You are beaten.", MAIEV_SHADOWSONG, 6000, 0, true}, + { // Emote dead for now. Kill him later + 11478, "You have won... Maiev...but the huntress... is nothing...without the hunt... you... are nothing... without me..", ILLIDAN_STORMRAGE, 22000, 65, true + }, + {11497, "He is right. I feel nothing... I am nothing... Farewell, champions.", MAIEV_SHADOWSONG, 9000, 0, true}, + {11498, NULL, MAIEV_SHADOWSONG, 0, true}, + {11387, "The Light will fill these dismal halls once again. I swear it.", AKAMA, 8000, 0, true}, + {0, NULL, EMPTY, 1000, 0, false} +}; + +static Yells RandomTaunts[]= +{ + {11467, "I can feel your hatred.", ILLIDAN_STORMRAGE, 0, 0, false}, + {11468, "Give in to your fear!", ILLIDAN_STORMRAGE, 0, 0, false}, + {11469, "You know nothing of power!", ILLIDAN_STORMRAGE, 0, 0, false}, + {11471, "Such... arrogance!", ILLIDAN_STORMRAGE, 0, 0, false} +}; + +static Yells MaievTaunts[]= +{ + {11493, "That is for Naisha!", MAIEV_SHADOWSONG, 0, false}, + {11494, "Bleed as I have bled!", MAIEV_SHADOWSONG, 0, 0, false}, + {11495, "There shall be no prison for you this time!", MAIEV_SHADOWSONG, 0, 0, false}, + {11500, "Meet your end, demon!", MAIEV_SHADOWSONG, 0, 0, false} +}; + +struct Locations +{ + float x, y, z; + uint32 id; +}; + +static Locations GlaivePosition[]= +{ + {695.105, 305.303, 354.256}, + {659.338, 305.303, 354.256}, + {700.105, 305.303, 354.256}, + {664.338, 305.303, 354.256} +}; + +static Locations EyeBlast[]= +{ + {650.697, 320.128, 353.730}, + {652.799, 275.091, 353.367}, + {701.527, 273.815, 353.230}, + {709.865, 325.654, 353.322} +}; + +static Locations AkamaWP[]= +{ + { 770.01, 304.50, 312.29 }, // Bottom of the first stairs, at the doors + { 780.66, 304.50, 319.74 }, // Top of the first stairs + { 790.13, 319.68, 319.76 }, // Bottom of the second stairs (left from the entrance) + { 787.17, 347.38, 341.42 }, // Top of the second stairs + { 781.34, 350.31, 341.44 }, // Bottom of the third stairs + { 762.60, 361.06, 353.60 }, // Top of the third stairs + { 756.35, 360.52, 353.27 }, // Before the door-thingy + { 743.82, 342.21, 353.00 }, // Somewhere further + { 732.69, 305.13, 353.00 }, // In front of Illidan + { 738.11, 365.44, 353.00 }, // in front of the door-thingy (the other one!) + { 792.18, 366.62, 341.42 }, // Down the first flight of stairs + { 796.84, 304.89, 319.76 }, // Down the second flight of stairs + { 782.01, 304.55, 319.76 } // Final location - back at the initial gates. This is where he will fight the minions! +}; +// 755.762, 304.0747, 312.1769 -- This is where Akama should be spawned +static Locations SpiritSpawns[]= +{ + {755.5426, 309.9156, 312.2129, SPIRIT_OF_UDALO}, + {755.5426, 298.7923, 312.0834, SPIRIT_OF_OLUM} +}; + +struct WayPoints +{ + WayPoints(uint32 _id, float _x, float _y, float _z) + { + id = _id; + x = _x; + y = _y; + z = _z; + } + uint32 id; + float x, y, z; +}; + +struct Animation // For the demon transformation +{ + uint32 aura, unaura, timer, size, displayid, phase; + bool equip; +}; + +static Animation DemonTransformation[]= +{ + {SPELL_DEMON_TRANSFORM_1, 0, 1300, 0, 0, 6, true}, + {SPELL_DEMON_TRANSFORM_2, SPELL_DEMON_TRANSFORM_1, 4000, 0, 0, 6, true}, + {SPELL_DEMON_FORM, 0, 3000, 1073741824, 21322, 6, false}, + {SPELL_DEMON_TRANSFORM_3, SPELL_DEMON_TRANSFORM_2, 3500, 0, 0, 6, false}, + {0, 0, 0, 0, 0, 4, false}, + {SPELL_DEMON_TRANSFORM_1, 0, 1500, 0, 0, 6, false}, + {SPELL_DEMON_TRANSFORM_2, SPELL_DEMON_TRANSFORM_1, 4000, 0, 0, 6, false}, + {0, SPELL_DEMON_FORM, 3000, 1069547520, 21135, 6, false}, + {SPELL_DEMON_TRANSFORM_3, SPELL_DEMON_TRANSFORM_2, 3500, 0, 0, 6, true}, + {0, 0, 0, 0, 0, 8, true} +}; + +/**** Demon Fire will be used for Eye Blast. Illidan needs to have access to it's vars and functions, so we'll set it here ****/ +struct MANGOS_DLL_DECL demonfireAI : public ScriptedAI +{ + demonfireAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 IllidanGUID; + + bool IsTrigger; + + uint32 CheckTimer; + uint32 DemonFireTimer; + uint32 DespawnTimer; + + void Reset() + { + IllidanGUID = 0; + + IsTrigger = false; + + CheckTimer = 2000; + DemonFireTimer = 0; + DespawnTimer = 45000; + } + + void Aggro(Unit *who) {} + void AttackStart(Unit* who) { } + void MoveInLineOfSight(Unit *who){ } + + void UpdateAI(const uint32 diff) + { + if(IsTrigger) + return; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if(CheckTimer < diff) + { + if(!IllidanGUID && pInstance) + { + IllidanGUID = pInstance->GetData64(DATA_ILLIDANSTORMRAGE); + if(IllidanGUID) + { + Unit* Illidan = Unit::GetUnit((*m_creature), IllidanGUID); + if(Illidan && !Illidan->HasUnitMovementFlag(MOVEMENTFLAG_LEVITATING)) + m_creature->setDeathState(JUST_DIED); + } + } + CheckTimer = 2000; + }else CheckTimer -= diff; + + if(DemonFireTimer < diff) + { + DoCast(m_creature, SPELL_DEMON_FIRE); + DemonFireTimer = 30000; + }else DemonFireTimer -= diff; + + if(DespawnTimer < diff) + m_creature->setDeathState(JUST_DIED); + else DespawnTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +/******* Functions and vars for Akama's AI ******/ +struct MANGOS_DLL_SPEC npc_akama_illidanAI : public ScriptedAI +{ + npc_akama_illidanAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + WayPointList.clear(); + Reset(); + } + + /* Instance Data */ + ScriptedInstance* pInstance; + + /* Timers */ + uint32 ChannelTimer; + uint32 TalkTimer; + uint32 WalkTimer; + uint32 SummonMinionTimer; + + /* GUIDs */ + uint64 IllidanGUID; + uint64 PlayerGUID; + uint64 SpiritGUID[2]; + uint64 ChannelGUID; + + bool IsTalking; + bool StartChanneling; + bool DoorOpen; + bool FightMinions; + bool IsReturningToIllidan; + bool IsWalking; + uint32 TalkCount; + uint32 ChannelCount; + + std::list WayPointList; + std::list::iterator WayPoint; + + void BeginEvent(uint64 PlayerGUID); + void Aggro(Unit *who) {} + + void Reset() + { + if(pInstance) + { + pInstance->SetData(DATA_ILLIDANSTORMRAGEEVENT, NOT_STARTED); + GameObject* Gate = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_ILLIDAN_GATE)); + if( Gate && !Gate->GetGoState() ) + Gate->SetGoState(1); // close door if already open (when raid wipes or something) + + for(uint8 i = DATA_GAMEOBJECT_ILLIDAN_DOOR_R; i < DATA_GAMEOBJECT_ILLIDAN_DOOR_L + 1; ++i) + { + GameObject* Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(i)); + if(Door) + Door->SetGoState(0); + } + } + + IllidanGUID = 0; + PlayerGUID = 0; + ChannelGUID = 0; + for(uint8 i = 0; i < 2; ++i) SpiritGUID[i] = 0; + + ChannelTimer = 0; + ChannelCount = 0; + SummonMinionTimer = 2000; + + WalkTimer = 0; + + TalkTimer = 0; + TalkCount = 0; + + KillAllElites(); + + IsReturningToIllidan = false; + FightMinions = false; + IsTalking = false; + StartChanneling = false; + DoorOpen = false; + + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, 0); // Database sometimes has strange values.. + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->SetVisibility(VISIBILITY_ON); + } + + // Do not call reset in Akama's evade mode, as this will stop him from summoning minions after he kills the first bit + void EnterEvadeMode() + { + InCombat = false; + + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(); + } + + void KillAllElites() + { + std::list::iterator itr; + for(itr = m_creature->getThreatManager().getThreatList().begin(); itr != m_creature->getThreatManager().getThreatList().end(); ++itr) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid()); + if(pUnit && (pUnit->GetTypeId() == TYPEID_UNIT) && (pUnit->GetEntry() == ILLIDARI_ELITE)) + pUnit->setDeathState(JUST_DIED); + } + } + + void ReturnToIllidan() + { + KillAllElites(); + InCombat = false; + FightMinions = false; + IsReturningToIllidan = true; + WayPoint = WayPointList.begin(); + m_creature->SetSpeed(MOVE_RUN, 2.0f); + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + IsWalking = true; + } + + void AddWaypoint(uint32 id, float x, float y, float z) + { + WayPoints AkamaWP(id, x, y, z); + WayPointList.push_back(AkamaWP); + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if(damage > m_creature->GetHealth() && (done_by->GetGUID() != m_creature->GetGUID())) + { + damage = 0; + DoCast(m_creature, SPELL_HEALING_POTION); + } + } + + void BeginDoorEvent(Player* player) + { + if(!pInstance) + return; + + outstring_log("SD2: Akama - Door event initiated by player %s", player->GetName()); + PlayerGUID = player->GetGUID(); + + GameObject* Gate = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_ILLIDAN_GATE)); + if(Gate) + { + float x,y,z; + Gate->GetPosition(x, y, z); + Creature* Channel = m_creature->SummonCreature(ILLIDAN_DOOR_TRIGGER, x, y, z+5, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 360000); + if(Channel) + { + ChannelGUID = Channel->GetGUID(); + // Invisible but spell visuals can still be seen. + Channel->SetUInt32Value(UNIT_FIELD_DISPLAYID, 11686); + Channel->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + float PosX, PosY, PosZ; + m_creature->GetPosition(PosX, PosY, PosZ); + for(uint8 i = 0; i < 2; ++i) + { + Creature* Spirit = m_creature->SummonCreature(SpiritSpawns[i].id, SpiritSpawns[i].x, SpiritSpawns[i].y, SpiritSpawns[i].z, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 360000); + if(Spirit) + { + Spirit->SetVisibility(VISIBILITY_OFF); + SpiritGUID[i] = Spirit->GetGUID(); + } + } + StartChanneling = true; + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + DoCast(Channel, SPELL_AKAMA_DOOR_FAIL); + } + } + } + + void MovementInform(uint32 type, uint32 id) + { + if(type != POINT_MOTION_TYPE || !IsWalking) + return; + + if(WayPoint->id != id) + return; + + switch(id) + { + case 6: + if(!IsReturningToIllidan) + { // open the doors that close the summit + for(uint32 i = DATA_GAMEOBJECT_ILLIDAN_DOOR_R; i < DATA_GAMEOBJECT_ILLIDAN_DOOR_L+1; ++i) + { + GameObject* Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(i)); + if(Door) + Door->SetGoState(0); + } + } + case 7: + if(IsReturningToIllidan) + { + IsWalking = false; + if(IllidanGUID) + { + Unit* Illidan = Unit::GetUnit((*m_creature), IllidanGUID); + if(Illidan) + { + float dx = Illidan->GetPositionX() + rand()%15; + float dy = Illidan->GetPositionY() + rand()%15; + m_creature->GetMotionMaster()->MovePoint(13, dx, dy, Illidan->GetPositionZ()); + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, IllidanGUID); + } + } + } + break; + case 8: + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + if(!IsReturningToIllidan) + { + IsWalking = false; + BeginEvent(PlayerGUID); + } + break; + case 12: + IsWalking = false; + FightMinions = true; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + break; + } + + ++WayPoint; + WalkTimer = 200; + } + + void DeleteFromThreatList() + { + if(!IllidanGUID) return; // If we do not have Illidan's GUID, do not proceed + // Create a pointer to Illidan + Creature* Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID)); + if(!Illidan) return; // No use to continue if Illidan does not exist + std::list::iterator itr = Illidan->getThreatManager().getThreatList().begin(); + for( ; itr != Illidan->getThreatManager().getThreatList().end(); ++itr) + { + // Loop through threatlist till our GUID is found in it. + if((*itr)->getUnitGuid() == m_creature->GetGUID()) + { + (*itr)->removeReference(); // Delete ourself from his threatlist. + return; // No need to continue anymore. + } + } + + // Now we delete our threatlist to prevent attacking anyone for now + m_creature->DeleteThreatList(); + } + + void UpdateAI(const uint32 diff) + { + if(IllidanGUID) + { + Creature* Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID)); + if(Illidan) + { + if(Illidan->IsInEvadeMode() && !m_creature->IsInEvadeMode()) + EnterEvadeMode(); + + if(((Illidan->GetHealth()*100 / Illidan->GetMaxHealth()) < 85) && InCombat && !FightMinions) + { + if(TalkTimer < diff) + { + switch(TalkCount) + { + case 0: + Illidan->Yell(SAY_AKAMA_MINION, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(Illidan, SOUND_AKAMA_MINION); + TalkTimer = 8000; + TalkCount = 1; + break; + case 1: + DoYell(SAY_AKAMA_LEAVE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AKAMA_LEAVE); + TalkTimer = 3000; + TalkCount = 2; + break; + case 2: + IsTalking = true; + TalkTimer = 2000; + m_creature->RemoveAllAuras(); + m_creature->CombatStop(); + m_creature->AttackStop(); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + TalkCount = 3; + break; + case 3: + DeleteFromThreatList(); + IsWalking = true; + WayPoint = WayPointList.begin(); + std::advance(WayPoint, 9); + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + break; + } + }else TalkTimer -= diff; + } + + if(((Illidan->GetHealth()*100 / Illidan->GetMaxHealth()) < 4) && !IsReturningToIllidan) + ReturnToIllidan(); + } + }else + { + if(pInstance) + IllidanGUID = pInstance->GetData64(DATA_ILLIDANSTORMRAGE); + } + + if(IsWalking && WalkTimer) + { + if(WalkTimer <= diff) + { + if(WayPoint == WayPointList.end()) + return; + m_creature->GetMotionMaster()->MovePoint(WayPoint->id, WayPoint->x, WayPoint->y,WayPoint->z); + WalkTimer = 0; + }else WalkTimer -= diff; + } + + if(StartChanneling) + { + if(ChannelTimer < diff) + { + switch(ChannelCount) + { + case 3: + if(!DoorOpen) + { + m_creature->InterruptNonMeleeSpells(true); + for(uint8 i = 0; i < 2; ++i) + { + if(SpiritGUID[i]) + { + Unit* Spirit = Unit::GetUnit((*m_creature), SpiritGUID[i]); + if(Spirit) + Spirit->InterruptNonMeleeSpells(true); + } + } + GameObject* Gate = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_ILLIDAN_GATE)); + if(Gate) + Gate->SetGoState(0); + ChannelCount++; + ChannelTimer = 5000; + } + break; + case 4: + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + ChannelTimer = 2000; + ChannelCount++; + break; + case 5: + DoYell(SAY_AKAMA_BEWARE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AKAMA_BEWARE); + if(ChannelGUID) + { + Unit* ChannelTarget = Unit::GetUnit((*m_creature), ChannelGUID); + if(ChannelTarget) + ChannelTarget->setDeathState(JUST_DIED); + } + for(uint8 i = 0; i < 2; ++i) + { + if(SpiritGUID[i]) + { + Unit* Spirit = Unit::GetUnit((*m_creature), SpiritGUID[i]); + if(Spirit) + Spirit->setDeathState(JUST_DIED); + } + } + ChannelTimer = 6000; + ChannelCount++; + break; + case 6: + StartChanneling = false; + if(WayPointList.empty()) + { + error_log("SD2: Akama has no waypoints to start with!"); + return; + } + + WayPoint = WayPointList.begin(); + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + m_creature->GetMotionMaster()->MovePoint(WayPoint->id, WayPoint->x, WayPoint->y, WayPoint->z); + IsWalking = true; + break; + default: + if(ChannelGUID) + { + Unit* Channel = Unit::GetUnit((*m_creature), ChannelGUID); + if(Channel) + { + m_creature->InterruptNonMeleeSpells(true); + + for(uint8 i = 0; i < 2; ++i) + { + if(SpiritGUID[i]) + { + Unit* Spirit = Unit::GetUnit((*m_creature), SpiritGUID[i]); + if(Spirit) + { + Spirit->InterruptNonMeleeSpells(true); + if(ChannelCount%2 == 0) + { + Spirit->CastSpell(Channel, SPELL_DEATHSWORN_DOOR_CHANNEL,false); + DoCast(Channel, SPELL_AKAMA_DOOR_CHANNEL); + } + else + { + if(Spirit->GetVisibility() == VISIBILITY_OFF) + Spirit->SetVisibility(VISIBILITY_ON); + } + } + } + } + if(ChannelCount < 3) + ChannelCount++; + ChannelTimer = 10000; + } + break; + } + } + }else ChannelTimer -= diff; + } + + if(FightMinions) + { + if(SummonMinionTimer < diff) + { + if(IllidanGUID) + { + Creature* Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID)); + if(!Illidan || Illidan->IsInEvadeMode()) + { + Reset(); + EnterEvadeMode(); + return; + } + } + + float x,y,z; + m_creature->GetPosition(x,y,z); + Creature* Elite = m_creature->SummonCreature(ILLIDARI_ELITE, x+rand()%10, y+rand()%10, z, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000); + if(Elite) + { + Elite->AI()->AttackStart(m_creature); + Elite->AddThreat(m_creature, 1000000.0f); + AttackStart(Elite); + } + SummonMinionTimer = 10000 + rand()%6000; + }else SummonMinionTimer -= diff; + } + + // If we don't have a target, or is talking, or has run away, return + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) return; + + DoMeleeAttackIfReady(); + } +}; + +/************* Custom check used for Agonizing Flames ***************/ +class AgonizingFlamesTargetCheck +{ + public: + AgonizingFlamesTargetCheck(Unit const* unit) : pUnit(unit) {} + bool operator() (Player* plr) + { + // Faster than square rooting + if(!plr->isGameMaster() && pUnit->GetDistance2d(plr) > 225) + return true; + + return false; + } + + private: + Unit const* pUnit; +}; + +/************************************** Illidan's AI ***************************************/ +struct MANGOS_DLL_SPEC boss_illidan_stormrageAI : public ScriptedAI +{ + boss_illidan_stormrageAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + /** Instance Data **/ + ScriptedInstance* pInstance; + + /** Generic **/ + bool IsTalking; + bool HasSummoned; + bool RefaceVictim; + bool InformAkama; + uint32 Phase; + uint32 GlobalTimer; + uint32 TalkCount; + uint32 DemonFormSequence; + + /** GUIDs **/ + uint64 FlameGUID[2]; + uint64 GlaiveGUID[2]; + uint64 AkamaGUID; + uint64 MaievGUID; + + /** Timers **/ + uint32 ShearTimer; + uint32 DrawSoulTimer; + uint32 FlameCrashTimer; + uint32 ParasiticShadowFiendTimer; + uint32 FireballTimer; + uint32 EyeBlastTimer; + uint32 DarkBarrageTimer; + uint32 SummonBladesTimer; // Animate summoning the Blades of Azzinoth in Phase 2 + uint32 SummonFlamesTimer; // Summon Flames of Azzinoth in Phase 2 + uint32 CheckFlamesTimer; // This is used to check the status of the Flames to see if we should begin entering Phase 3 or not. + uint32 RetrieveBladesTimer; // Animate retrieving the Blades of Azzinoth in Phase 2 -> 3 transition + uint32 LandTimer; // This is used at the end of phase 2 to signal Illidan landing after Flames are dead + uint32 AgonizingFlamesTimer; + uint32 ShadowBlastTimer; + uint32 FlameBurstTimer; + uint32 ShadowDemonTimer; + uint32 TalkTimer; + uint32 TransformTimer; + uint32 EnrageTimer; + uint32 CageTimer; + uint32 LayTrapTimer; + uint32 AnimationTimer; + uint32 TauntTimer; // This is used for his random yells + uint32 FaceVictimTimer; + uint32 BerserkTimer; + + void Reset() + { + Phase = PHASE_NORMAL; + + // Check if any flames/glaives are alive/existing. Kill if alive and set GUIDs to 0 + for(uint8 i = 0; i < 2; i++) + { + if(FlameGUID[i]) + { + Unit* Flame = Unit::GetUnit((*m_creature), FlameGUID[i]); + if(Flame) + Flame->setDeathState(JUST_DIED); + FlameGUID[i] = 0; + } + + if(GlaiveGUID[i]) + { + Unit* Glaive = Unit::GetUnit((*m_creature), GlaiveGUID[i]); + if(Glaive) + Glaive->setDeathState(JUST_DIED); + GlaiveGUID[i] = 0; + } + } + + if(AkamaGUID) + { + Creature* Akama = ((Creature*)Unit::GetUnit((*m_creature), AkamaGUID)); + if(Akama) + { + if(!Akama->isAlive()) + Akama->Respawn(); + ((npc_akama_illidanAI*)Akama->AI())->Reset(); + ((npc_akama_illidanAI*)Akama->AI())->EnterEvadeMode(); + Akama->GetMotionMaster()->MoveTargetedHome(); + } + } + + InformAkama = false; + RefaceVictim = false; + HasSummoned = false; + AkamaGUID = 0; + MaievGUID = 0; + + FaceVictimTimer = 1000; + BerserkTimer = 1500000; + GlobalTimer = 0; + DemonFormSequence = 0; + + /** Normal Form **/ + ShearTimer = 20000 + (rand()%11 * 1000); // 20 to 30 seconds + FlameCrashTimer = 30000; //30 seconds + ParasiticShadowFiendTimer = 25000; // 25 seconds + DrawSoulTimer = 50000; // 50 seconds + + /** Phase 2 **/ + SummonBladesTimer = 10000; + SummonFlamesTimer = 20000; // Phase 2 timers may be incorrect + FireballTimer = 5000; + DarkBarrageTimer = 45000; + EyeBlastTimer = 30000; + CheckFlamesTimer = 5000; + RetrieveBladesTimer = 5000; + LandTimer = 0; + + /** Phase 3+ **/ + AgonizingFlamesTimer = 35000; // Phase 3+ timers may be incorrect + ShadowBlastTimer = 3000; + FlameBurstTimer = 10000; + ShadowDemonTimer = 30000; + TransformTimer = 90000; + EnrageTimer = 40000; + CageTimer = 30000; + LayTrapTimer = CageTimer + 2000; + AnimationTimer = 0; + + TauntTimer = 30000; // This timer may be off. + + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, 21135); + m_creature->InterruptNonMeleeSpells(false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + // Unequip warglaives if needed + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 0); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 0); + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_LEVITATING); + + IsTalking = false; + + TalkCount = 0; + TalkTimer = 0; + + if(pInstance) + pInstance->SetData(DATA_ILLIDANSTORMRAGEEVENT, NOT_STARTED); + } + + void Aggro(Unit *who) { DoZoneInCombat(); } + + void AttackStart(Unit *who) + { + if(!who || IsTalking || Phase == 2 || Phase == 4 || Phase == 6 || m_creature->HasAura(SPELL_KNEEL, 0)) + return; + + if (who->isTargetableForAttack() && who!= m_creature) + { + //Begin melee attack if we are within range + DoStartAttackAndMovement(who); + } + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim() || IsTalking || m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + } + } + } + + void JustDied(Unit *killer) + { + IsTalking = false; + TalkCount = 0; + TalkTimer = 0; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if(!pInstance) + return; + // Completed + pInstance->SetData(DATA_ILLIDANSTORMRAGEEVENT, DONE); + + for(uint8 i = DATA_GAMEOBJECT_ILLIDAN_DOOR_R; i < DATA_GAMEOBJECT_ILLIDAN_DOOR_L + 1; ++i) + { + GameObject* Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(i)); + if(Door) + Door->SetGoState(0); // Open Doors + } + + } + + void KilledUnit(Unit *victim) + { + if(victim == m_creature) return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL1, LANG_UNIVERSAL, victim); + DoPlaySoundToSet(m_creature, SOUND_KILL1); + break; + case 1: + DoYell(SAY_KILL2, LANG_UNIVERSAL, victim); + DoPlaySoundToSet(m_creature, SOUND_KILL2); + break; + } + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if(damage > m_creature->GetHealth()) // Don't let ourselves be slain before we do our death speech + { + damage = 0; + m_creature->SetHealth(m_creature->GetMaxHealth()/100); + } + } + + void Cast(Unit* victim, uint32 Spell, bool triggered = false) + { + if(!victim) + return; + + RefaceVictim = true; + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID()); + m_creature->CastSpell(victim, Spell, triggered); + } + + /** This will handle the cast of eye blast **/ + void CastEyeBlast() + { + m_creature->InterruptNonMeleeSpells(false); + + DarkBarrageTimer += 10000; + + DoYell(SAY_EYE_BLAST, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_EYE_BLAST); + + uint32 initial = rand()%4; + uint32 final = 0; + if(initial < 3) + final = initial+1; + + float initial_X = EyeBlast[initial].x; + float initial_Y = EyeBlast[initial].y; + float initial_Z = EyeBlast[initial].z; + + float final_X = EyeBlast[final].x; + float final_Y = EyeBlast[final].y; + float final_Z = EyeBlast[final].z; + + for(uint8 i = 0; i < 2; ++i) + { + Creature* Trigger = NULL; + Trigger = m_creature->SummonCreature(DEMON_FIRE, initial_X, initial_Y, initial_Z, 0, TEMPSUMMON_TIMED_DESPAWN, 20000); + if(Trigger) + { + ((demonfireAI*)Trigger->AI())->IsTrigger = true; + Trigger->GetMotionMaster()->MovePoint(0, final_X, final_Y, final_Z); + + if(!i) + Trigger->CastSpell(Trigger, SPELL_EYE_BLAST_TRIGGER, true); + else + { + Trigger->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, Trigger->GetGUID()); + DoCast(Trigger, SPELL_EYE_BLAST); + } + } + } + } + + // It's only cast on players that are greater than 15 yards away from Illidan. If no one is found, cast it on MT instead (since selecting someone in that 15 yard radius would cause the flames to hit the MT anyway). + void CastAgonizingFlames() + { + // We'll use grid searching for this, using a custom searcher that selects a player that is at a distance >15 yards + Player* target = NULL; + + CellPair pair(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY())); + Cell cell(pair); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + AgonizingFlamesTargetCheck check(m_creature); + MaNGOS::PlayerSearcher searcher(target, check); + TypeContainerVisitor + , GridTypeMapContainer> visitor(searcher); + + CellLock cell_lock(cell, pair); + cell_lock->Visit(cell_lock, visitor, *(m_creature->GetMap())); + + if(target) + DoCast(target, SPELL_AGONIZING_FLAMES); + else + DoCast(m_creature->getVictim(), SPELL_AGONIZING_FLAMES); + } + + void Talk(uint32 count) + { + if(!m_creature->isAlive()) return; + uint32 sound = Conversation[count].sound; + char* text = NULL; + if(Conversation[count].text) + text = Conversation[count].text; + TalkTimer = Conversation[count].timer; + uint32 emote = Conversation[count].emote; + IsTalking = Conversation[count].Talk; + Creature* creature = NULL; + uint64 GUID = 0; + if(Conversation[count].creature == ILLIDAN_STORMRAGE) + creature = m_creature; + else if(Conversation[count].creature == AKAMA) + { + if(!AkamaGUID) + { + if(pInstance) + { + AkamaGUID = pInstance->GetData64(DATA_AKAMA); + if(!AkamaGUID) + return; + GUID = AkamaGUID; + } + } + else GUID = AkamaGUID; + } + else if(Conversation[count].creature == MAIEV_SHADOWSONG) + { + if(!MaievGUID) + return; + GUID = MaievGUID; + } + else if(Conversation[count].creature == EMPTY) // This is just for special cases without speech/sounds/emotes. + return; + + if(GUID) // Now we check if we actually specified a GUID, if so: + // we grab a pointer to that creature + creature = ((Creature*)Unit::GetUnit((*m_creature), GUID)); + + if(creature) + { + creature->HandleEmoteCommand(emote); // Make the creature do some animation! + if(text) + creature->Yell(text, LANG_UNIVERSAL, 0); // Have the creature yell out some text + if(sound) + DoPlaySoundToSet(creature, sound); // Play some sound on the creature + } + } + + void Move(float X, float Y, float Z, Creature* _Creature) + { + _Creature->GetMotionMaster()->MovePoint(0, X, Y, Z); + } + + void HandleDemonTransformAnimation(uint32 count) + { + uint32 unaura = DemonTransformation[count].unaura; + uint32 aura = DemonTransformation[count].aura; + uint32 displayid = DemonTransformation[count].displayid; + AnimationTimer = DemonTransformation[count].timer; + uint32 size = DemonTransformation[count].size; + + m_creature->InterruptNonMeleeSpells(false); + + if(DemonTransformation[count].phase != 8) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + } + + if(unaura) + m_creature->RemoveAurasDueToSpell(unaura); + + if(aura) + DoCast(m_creature, aura, true); + + if(displayid) + // It's morphin time! + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, displayid); + /*if(size) + m_creature->SetUInt32Value(OBJECT_FIELD_SCALE_X, size); // Let us grow! (or shrink)*/ + + if(DemonTransformation[count].equip) + { + // Requip warglaives if needed + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 45479); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 45481); + } + else + { + // Unequip warglaives if needed + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 0); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 0); + } + + if(DemonTransformation[count].phase != 8) + Phase = DemonTransformation[count].phase; // Set phase properly + else + { + // Refollow and attack our old victim + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + if(MaievGUID) Phase = PHASE_NORMAL_MAIEV; // Depending on whether we summoned Maiev, we switch to either phase 5 or 3 + else Phase = PHASE_NORMAL_2; + } + + if(count == 7) + { + DoResetThreat(); + m_creature->RemoveAurasDueToSpell( SPELL_DEMON_FORM ); + } + else if(count == 4) + { + DoResetThreat(); + if(!m_creature->HasAura(SPELL_DEMON_FORM, 0)) + DoCast(m_creature, SPELL_DEMON_FORM, true); + } + } + + /** To reduce the amount of code in UpdateAI, we can seperate them into different functions and simply call them from UpdateAI **/ + void EnterPhase2() + { + DoYell(SAY_TAKEOFF, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_TAKEOFF); + + SummonBladesTimer = 10000; // Summon Glaives when this decrements + SummonFlamesTimer = 20000; // Summon Flames when this decrements + GlobalTimer += 20000; + LandTimer = 0; + Phase = PHASE_FLIGHT; + m_creature->RemoveAllAuras(); + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0); + // So players don't shoot us down + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + // Animate our take off! + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + // We now hover! + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING); + m_creature->GetMotionMaster()->MovePoint(0, CENTER_X, CENTER_Y, CENTER_Z); + for(uint8 i = 0; i < 2; ++i) + { + Creature* Glaive = m_creature->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + if(Glaive) + { + GlaiveGUID[i] = Glaive->GetGUID(); // We need this to remove them later on + Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + Glaive->SetVisibility(VISIBILITY_OFF); + Glaive->setFaction(m_creature->getFaction()); + } + } + } + + void SummonBladesOfAzzinoth() + { + m_creature->GetMotionMaster()->Clear(false); + + LandTimer = 0; + RetrieveBladesTimer = 0; + + DoCast(m_creature, SPELL_THROW_GLAIVE2); // Make it look like we're throwing the glaives on the ground + // We no longer wear the glaives! + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 0); + // since they are now channeling the flames (or will be) + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 0); + for(uint8 i = 0; i < 2; ++i) + { + Creature* Glaive = NULL; + Glaive = ((Creature*)Unit::GetUnit((*m_creature), GlaiveGUID[i])); + if(Glaive) + { + DoCast(Glaive, SPELL_THROW_GLAIVE, true); + Glaive->SetVisibility(VISIBILITY_ON); + } + } + } + + void SummonFlamesOfAzzinoth() + { + DoYell(SAY_SUMMONFLAMES, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SUMMONFLAMES); + + for(uint8 i = 0; i < 2; ++i) + { + Creature* Flame = NULL; + Creature* Glaive = NULL; + Glaive = ((Creature*)Unit::GetUnit((*m_creature), GlaiveGUID[i])); + if(Glaive) + { + Flame = m_creature->SummonCreature(FLAME_OF_AZZINOTH, GlaivePosition[i+2].x, GlaivePosition[i+2].y, GlaivePosition[i+2].z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000); + if(Flame) + { + // Just in case the database has it as a different faction + Flame->setFaction(m_creature->getFaction()); + // Attack our target! + Flame->AI()->AttackStart(m_creature->getVictim()); + FlameGUID[i] = Flame->GetGUID(); // Record GUID in order to check if they're dead later on to move to the next phase + // Glaives do some random Beam type channel on it. + Glaive->CastSpell(Flame, SPELL_AZZINOTH_CHANNEL, true); + if(m_creature->getVictim()) + Flame->AI()->AttackStart(m_creature->getVictim()); + } + else + { + DoTextEmote("is unable to summon a Flame of Azzinoth.", NULL); + error_log("SD2 ERROR: Illidan Stormrage AI: Unable to summon Flame of Azzinoth (entry: 22997), please check your database"); + EnterEvadeMode(); + } + } + else + { + DoTextEmote("is unable to summon a Blade of Azzinoth.", NULL); + error_log("SD2 ERROR: Illidan Stormrage AI: Unable to summon Blade of Azzinoth (entry: 22996), please check your database"); + } + } + DoResetThreat(); // And now reset our threatlist + HasSummoned = true; + } + + void SummonMaiev() + { + TauntTimer += 4000; + GlobalTimer += 4000; + + m_creature->InterruptNonMeleeSpells(false); // Interrupt any of our spells + Creature* Maiev = NULL; // Summon Maiev near Illidan + Maiev = m_creature->SummonCreature(MAIEV_SHADOWSONG, m_creature->GetPositionX() + 10, m_creature->GetPositionY() + 5, m_creature->GetPositionZ()+2, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000); + if(Maiev) + { + m_creature->GetMotionMaster()->Clear(false); // Stop moving, it's rude to walk and talk! + m_creature->GetMotionMaster()->MoveIdle(); + // Just in case someone is unaffected by Shadow Prison + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + DoCast(m_creature, SPELL_SHADOW_PRISON, true); + TalkCount = 10; + IsTalking = true; // We are now talking/ + Maiev->SetVisibility(VISIBILITY_OFF); // Leave her invisible until she has to talk + Maiev->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + MaievGUID = Maiev->GetGUID(); + } + else // If Maiev cannot be summoned, reset the encounter and post some errors to the console. + { + EnterEvadeMode(); + DoTextEmote("is unable to summon Maiev Shadowsong and enter Phase 4. Resetting Encounter.", NULL); + error_log("SD2 ERROR: Unable to summon Maiev Shadowsong (entry: 23197). Check your database to see if you have the proper SQL for Maiev Shadowsong (entry: 23197)"); + } + } + + void InitializeDeath() + { + m_creature->RemoveAllAuras(); + DoCast(m_creature, SPELL_DEATH); // Animate his kneeling + stun him + // Don't let the players interrupt our talk! + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->GetMotionMaster()->Clear(false); // No moving! + m_creature->GetMotionMaster()->MoveIdle(); + if(MaievGUID) + { + Creature* Maiev = ((Creature*)Unit::GetUnit((*m_creature), MaievGUID)); + if(Maiev) + { + Maiev->CombatStop(); // Maiev shouldn't do anything either. No point in her attacking us =] + Maiev->GetMotionMaster()->Clear(false); // Stop her from moving as well + Maiev->GetMotionMaster()->MoveIdle(); + float distance = 10.0f; + float dx = m_creature->GetPositionX() + (distance*cos(m_creature->GetOrientation())); + float dy = m_creature->GetPositionY() + (distance*sin(m_creature->GetOrientation())); + Maiev->Relocate(dx,dy,Maiev->GetPositionZ()); + Maiev->SendMonsterMove(dx,dy,Maiev->GetPositionZ(), 0, 0, 0); + Maiev->CastSpell(Maiev, SPELL_TELEPORT_VISUAL, true); + Maiev->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->GetGUID()); + } + } + IsTalking = true; + TalkCount++; + } + + void UpdateAI(const uint32 diff) + { + /*** This section will handle the conversations ***/ + if(IsTalking) // Somewhat more efficient using a function rather than a long switch + { + if(TalkTimer < diff) + { + switch(TalkCount) // This is only for specialized cases + { + case 0: + // Time to stand up! + m_creature->RemoveAurasDueToSpell( SPELL_KNEEL ); + break; + case 8: + // Equip our warglaives! + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 45479); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 45481); + m_creature->setFaction(14); // Hostile if we weren't before + break; + case 9: + if(AkamaGUID) + { + Creature* Akama = ((Creature*)Unit::GetUnit((*m_creature), AkamaGUID)); + if(Akama) + { + Akama->GetMotionMaster()->Clear(false); + // Akama runs to us! + Akama->GetMotionMaster()->MoveChase(m_creature); + m_creature->GetMotionMaster()->Clear(false); + // We run to Akama! + m_creature->GetMotionMaster()->MoveChase(Akama); + Akama->AddThreat(m_creature, 1000000.0f); + AttackStart(Akama); // Start attacking Akama + ((npc_akama_illidanAI*)Akama->AI())->IsTalking = false; + // Akama starts attacking us + ((npc_akama_illidanAI*)Akama->AI())->AttackStart(m_creature); + } + } + // We are now attackable! + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + break; + case 11: + if(MaievGUID) + { + Unit* Maiev = Unit::GetUnit((*m_creature), MaievGUID); + if(Maiev) + { + // Maiev is now visible + Maiev->SetVisibility(VISIBILITY_ON); + // onoz she looks like she teleported! + Maiev->CastSpell(Maiev, SPELL_TELEPORT_VISUAL, true); + // Have her face us + Maiev->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->GetGUID()); + // Face her, so it's not rude =P + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, Maiev->GetGUID()); + } + } + break; + case 14: + if(MaievGUID) + { + Creature* Maiev = ((Creature*)Unit::GetUnit((*m_creature), MaievGUID)); + if(Maiev) + { + Maiev->GetMotionMaster()->Clear(false); + Maiev->GetMotionMaster()->MoveChase(m_creature); + // Have Maiev add a lot of threat on us so that players don't pull her off if they damage her via AOE + Maiev->AddThreat(m_creature, 10000000.0f); + // Force Maiev to attack us. + Maiev->AI()->AttackStart(m_creature); + Maiev->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + IsTalking = false; + FaceVictimTimer = 2000; + RefaceVictim = true; + break; + case 20: // Kill ourself. + if(MaievGUID) + { + Creature* Maiev = ((Creature*)Unit::GetUnit((*m_creature), MaievGUID)); + if(Maiev) // Make Maiev leave + { + Maiev->CastSpell(Maiev, SPELL_TELEPORT_VISUAL, true); + Maiev->setDeathState(JUST_DIED); + } + } + IsTalking = false; + if(m_creature->getVictim()) + m_creature->getVictim()->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE,SPELL_SCHOOL_MASK_NORMAL, NULL, false); + else + // Now we kill ourself + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + break; + } + Talk(TalkCount); // This function does most of the talking + TalkCount++; + }else TalkTimer -= diff; + } + + // If we don't have a target, return. + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() || IsTalking) + return; + + // If we are 'caged', then we shouldn't do anything such as cast spells or transform into Demon Form. + if(m_creature->HasAura(SPELL_CAGED, 0)) + { + EnrageTimer = 40000; // Just so that he doesn't immediately enrage after he stops being caged. + CageTimer = 30000; + return; + } + + // Berserk Timer - flat 25 minutes + if(!m_creature->HasAura(SPELL_BERSERK, 0) && Phase != PHASE_DEMON_SEQUENCE) + if(BerserkTimer < diff) + { + DoYell(SAY_ENRAGE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + DoCast(m_creature, SPELL_BERSERK, true); + }else BerserkTimer -= diff; + + if(RefaceVictim) + if(FaceVictimTimer < diff) + { + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->getVictim()->GetGUID()); + FaceVictimTimer = 1000; + RefaceVictim = false; + }else FaceVictimTimer -= diff; + + /** Signal to change to phase 2 **/ + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 65) && (Phase == PHASE_NORMAL)) + EnterPhase2(); + + /** Signal to summon Maiev **/ + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 30) && !MaievGUID && + ((Phase != PHASE_DEMON) || (Phase != PHASE_DEMON_SEQUENCE))) + SummonMaiev(); + + /** Time for the death speech **/ + if((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 1) && (!IsTalking) && + ((Phase != PHASE_DEMON) || (Phase != PHASE_DEMON_SEQUENCE))) + InitializeDeath(); + + /***** Spells for Phase 1, 3 and 5 (Normal Form) ******/ + if(Phase == PHASE_NORMAL || Phase == PHASE_NORMAL_2 || Phase == PHASE_NORMAL_MAIEV) + { + if(TauntTimer < diff) // His random taunt/yell timer. + { + uint32 random = rand()%4; + char* yell = RandomTaunts[random].text; + uint32 soundid = RandomTaunts[random].sound; + if(yell) + DoYell(yell, LANG_UNIVERSAL, NULL); + if(soundid) + DoPlaySoundToSet(m_creature, soundid); + TauntTimer = 32000; + }else TauntTimer -= diff; + + if(GlobalTimer < diff) // Global Timer so that spells do not overlap. + { + if(ShearTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SHEAR); + ShearTimer = 25000 + (rand()%16 * 1000); + GlobalTimer += 2000; + }else ShearTimer -= diff; + + if(FlameCrashTimer < diff) + { + // It spawns multiple flames sometimes. Therefore, we'll do this manually. + //DoCast(m_creature->getVictim(), SPELL_FLAME_CRASH); + DoSpawnCreature(FLAME_CRASH, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 40000); + FlameCrashTimer = 35000; + GlobalTimer += 2000; + }else FlameCrashTimer -= diff; + + if(ParasiticShadowFiendTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,1); + if(target && target->isAlive() && !target->HasAura(SPELL_PARASITIC_SHADOWFIEND, 0)) + { + Cast(target, SPELL_PARASITIC_SHADOWFIEND); + ParasiticShadowFiendTimer = 40000; + } + }else ParasiticShadowFiendTimer -= diff; + + if(DrawSoulTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_DRAW_SOUL); + DrawSoulTimer = 55000; + GlobalTimer += 3000; + }else DrawSoulTimer -= diff; + }else GlobalTimer -= diff; + + if(!IsTalking) + DoMeleeAttackIfReady(); + } + + /*** Phase 2 ***/ + if(Phase == PHASE_FLIGHT) + { + // Check if we have summoned or not. + if(!HasSummoned) + { + if(SummonBladesTimer) + if(SummonBladesTimer <= diff) + { + SummonBladesOfAzzinoth(); + SummonBladesTimer = 0; + }else SummonBladesTimer -= diff; + + if(SummonFlamesTimer < diff) + { + SummonFlamesOfAzzinoth(); + }else SummonFlamesTimer -= diff; + } + + if(!m_creature->GetMotionMaster()->empty() && (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE)) + m_creature->GetMotionMaster()->Clear(false); + + if(HasSummoned) + { + if(CheckFlamesTimer) + if(CheckFlamesTimer <= diff) + { + // Check if flames are dead or non-existant. If so, set GUID to 0. + for(uint8 i = 0; i < 2; i++) + { + if(FlameGUID[i]) + { + Unit* Flame = NULL; + Flame = Unit::GetUnit((*m_creature), FlameGUID[i]); + + // If the flame dies, or somehow the pointer becomes invalid, reset GUID to 0. + if(!Flame || !Flame->isAlive()) + FlameGUID[i] = 0; + } + } + CheckFlamesTimer = 500; + }else CheckFlamesTimer -= diff; + + // If both flames are dead/non-existant, kill glaives and change to phase 3. + if(!FlameGUID[0] && !FlameGUID[1] && CheckFlamesTimer) + { + RetrieveBladesTimer = 5000; // Prepare for re-equipin! + CheckFlamesTimer = 0; + } + + if(RetrieveBladesTimer) + if(RetrieveBladesTimer <= diff) // Time to get back our glaives! + { + // Interrupt any spells we might be doing *cough* DArk Barrage *cough* + m_creature->InterruptNonMeleeSpells(false); + for(uint8 i = 0; i < 2; i++) + { + if(GlaiveGUID[i]) + { + Unit* Glaive = NULL; + Glaive = Unit::GetUnit((*m_creature), GlaiveGUID[i]); + if(Glaive) + { + // Make it look like the Glaive flies back up to us + Glaive->CastSpell(m_creature, SPELL_GLAIVE_RETURNS, true); + // Despawn the Glaive + Glaive->setDeathState(JUST_DIED); + } + GlaiveGUID[i] = 0; + } + } + // Re-equip our warblades! + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 45479); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 45481); + LandTimer = 5000; // Prepare for landin'! + RetrieveBladesTimer = 0; + }else RetrieveBladesTimer -= diff; + + if(LandTimer) + { + if(LandTimer <= diff) // Time to land! + { + DoResetThreat(); + // anndddd touchdown! + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT + MOVEMENTFLAG_LEVITATING); + Phase = PHASE_NORMAL_2; + // We should let the raid fight us =) + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->getVictim()->GetGUID()); + // Chase our victim! + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + }else LandTimer -= diff; + return; // Do not continue past this point if LandTimer is not 0 and we are in phase 2. + } + } + + if(GlobalTimer < diff) + { + if(FireballTimer < diff) + { + Cast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL); + FireballTimer = 5000; + }else FireballTimer -= diff; + + if(DarkBarrageTimer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_DARK_BARRAGE); + DarkBarrageTimer = 35000; + GlobalTimer += 9000; + }else DarkBarrageTimer -= diff; + + if(EyeBlastTimer < diff) + { + CastEyeBlast(); + EyeBlastTimer = 30000; + }else EyeBlastTimer -= diff; + }else GlobalTimer -= diff; + } + + /** Phase 3,5 spells only**/ + if(Phase == PHASE_NORMAL_2 || Phase == PHASE_NORMAL_MAIEV) + { + if(GlobalTimer < diff) + { + if(AgonizingFlamesTimer < diff) + { + CastAgonizingFlames(); + AgonizingFlamesTimer = 60000; + }else AgonizingFlamesTimer -= diff; + }else GlobalTimer -= diff; + + if(TransformTimer < diff) + { + uint32 CurHealth = m_creature->GetHealth()*100 / m_creature->GetMaxHealth(); + // Prevent Illidan from morphing if less than 32% or 5%, as this may cause issues with the phase transition or death speech + if((CurHealth < 32 && !MaievGUID) || (CurHealth < 5)) + return; + + Phase = PHASE_DEMON_SEQUENCE; // Transform sequence + DemonFormSequence = 0; + AnimationTimer = 0; + DoYell(SAY_MORPH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_MORPH); + TransformTimer = 60000; + FlameBurstTimer = 10000; + ShadowDemonTimer = 30000; + m_creature->GetMotionMaster()->Clear(false);// Stop moving + }else TransformTimer -= diff; + } + + /** Phase 4 spells only (Demon Form) **/ + if(Phase == PHASE_DEMON) + { + // Stop moving if we are by clearing movement generators. + if(!m_creature->GetMotionMaster()->empty()) + m_creature->GetMotionMaster()->Clear(false); + + if(TransformTimer < diff) + { + Phase = PHASE_DEMON_SEQUENCE; + DemonFormSequence = 5; + AnimationTimer = 100; + TransformTimer = 60000; + }else TransformTimer -= diff; + + if(ShadowDemonTimer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + Creature* ShadowDemon = NULL; + for(uint8 i = 0; i < 4; i++) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + // only on players. + if(target && target->GetTypeId() == TYPEID_PLAYER) + { + ShadowDemon = DoSpawnCreature(SHADOW_DEMON, 0,0,0,0,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,25000); + if(ShadowDemon) + { + ShadowDemon->AddThreat(target, 5000000.0f); + ShadowDemon->AI()->AttackStart(target); + DoZoneInCombat(ShadowDemon); + } + } + } + ShadowDemonTimer = 60000; + }else ShadowDemonTimer -= diff; + + if(GlobalTimer < diff) + { + if(ShadowBlastTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_TOPAGGRO, 0); + if(target && target->isAlive()) + { + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, target->GetGUID()); + DoCast(target, SPELL_SHADOW_BLAST); + ShadowBlastTimer = 4000; + GlobalTimer += 1500; + } + if(!m_creature->HasAura(SPELL_DEMON_FORM, 0)) + DoCast(m_creature, SPELL_DEMON_FORM, true); + }else ShadowBlastTimer -= diff; + + if(FlameBurstTimer < diff) + { + DoCast(m_creature, SPELL_FLAME_BURST); + FlameBurstTimer = 15000; + }else FlameBurstTimer -= diff; + }else GlobalTimer -= diff; + } + + /** Phase 5 timers. Enrage spell **/ + if(Phase == PHASE_NORMAL_MAIEV) + { + if(EnrageTimer < diff) + { + DoCast(m_creature, SPELL_ENRAGE); + EnrageTimer = 40000; + CageTimer = 30000; + TransformTimer += 10000; + }else EnrageTimer -= diff; + + // We'll handle Cage Trap in Illidan's script for simplicity's sake + if(CageTimer < diff) + { + if(MaievGUID) + { + Unit* Maiev = Unit::GetUnit((*m_creature), MaievGUID); + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(!Maiev || !target || (target->GetTypeId() != TYPEID_PLAYER)) + return; + float X, Y, Z; + target->GetPosition(X, Y, Z); + Maiev->Relocate(X, Y, Z, Maiev->GetOrientation()); + // Make it look like she 'teleported' + Maiev->CastSpell(Maiev, SPELL_TELEPORT_VISUAL, true); + // summon the trap! + Maiev->CastSpell(Maiev, SPELL_CAGE_TRAP_SUMMON, false); + } + CageTimer = 15000; + }else CageTimer -= diff; + } + + if(Phase == PHASE_DEMON_SEQUENCE) // Demonic Transformation + { + if(AnimationTimer < diff) + { + HandleDemonTransformAnimation(DemonFormSequence); + DemonFormSequence++; + }else AnimationTimer -= diff; + } + } +}; + +/*********************** End of Illidan AI ******************************************/ + +void npc_akama_illidanAI::BeginEvent(uint64 PlayerGUID) +{ + debug_log("SD2: Akama - Illidan Introduction started. Illidan event properly begun."); + if(pInstance) + { + IllidanGUID = pInstance->GetData64(DATA_ILLIDANSTORMRAGE); + pInstance->SetData(DATA_ILLIDANSTORMRAGEEVENT, IN_PROGRESS); + } + + if(pInstance) + for(uint8 i = DATA_GAMEOBJECT_ILLIDAN_DOOR_R; i < DATA_GAMEOBJECT_ILLIDAN_DOOR_L+1; ++i) + { + GameObject* Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(i)); + if(Door) + Door->SetGoState(1); + } + + if(IllidanGUID) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + Creature* Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID)); + if(Illidan) + { + Illidan->RemoveAurasDueToSpell(SPELL_KNEEL); // Time for Illidan to stand up. + // First line of Akama-Illidan convo + ((boss_illidan_stormrageAI*)Illidan->AI())->TalkCount = 0; + // Begin Talking + ((boss_illidan_stormrageAI*)Illidan->AI())->IsTalking = true; + ((boss_illidan_stormrageAI*)Illidan->AI())->AkamaGUID = m_creature->GetGUID(); + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, Illidan->GetGUID()); + Illidan->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->GetGUID()); + IsTalking = true; // Prevent Akama from starting to attack him + // Prevent players from talking again + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + Illidan->GetMotionMaster()->Clear(false); + Illidan->GetMotionMaster()->MoveIdle(); + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + + if(PlayerGUID) + { + Unit* player = Unit::GetUnit((*m_creature), PlayerGUID); + if(player) + Illidan->AddThreat(player, 100.0f); + } + } + } +} + +bool GossipSelect_npc_akama_at_illidan(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if(action == GOSSIP_ACTION_INFO_DEF) // Time to begin the event + { + player->CLOSE_GOSSIP_MENU(); + ((npc_akama_illidanAI*)_Creature->AI())->BeginDoorEvent(player); + } + return true; +} + +bool GossipHello_npc_akama_at_illidan(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(10465, _Creature->GetGUID()); + + return true; +} + +struct MANGOS_DLL_SPEC boss_maievAI : public ScriptedAI +{ + boss_maievAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + }; + + uint32 TauntTimer; + uint64 IllidanGUID; + + ScriptedInstance* pInstance; + + void Reset() + { + TauntTimer = 12000; + IllidanGUID = 0; + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if(!IllidanGUID) + { + if(pInstance) + IllidanGUID = pInstance->GetData64(DATA_ILLIDANSTORMRAGE); + }else + { + Creature* Illidan = NULL; + Illidan = ((Creature*)Unit::GetUnit((*m_creature), IllidanGUID)); + if(!Illidan || !Illidan->isAlive() || Illidan->IsInEvadeMode()) + { + m_creature->SetVisibility(VISIBILITY_OFF); + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + else if(Illidan && ((Illidan->GetHealth()*100 / Illidan->GetMaxHealth()) < 2)) + return; + } + + // Return if we don't have a target + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(TauntTimer < diff) + { + uint32 random = rand()%4; + char* text = MaievTaunts[random].text; + uint32 sound = MaievTaunts[random].sound; + DoYell(text, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, sound); + TauntTimer = 22000 + rand()%21 * 1000; + }else TauntTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL cage_trap_triggerAI : public ScriptedAI +{ + cage_trap_triggerAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint64 IllidanGUID; + uint64 CageTrapGUID; + + uint32 DespawnTimer; + + bool Active; + bool SummonedBeams; + + void Reset() + { + IllidanGUID = 0; + CageTrapGUID = 0; + + Active = false; + SummonedBeams = false; + + DespawnTimer = 0; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit *who){} + + void MoveInLineOfSight(Unit *who) + { + if(!Active) + return; + + if(who && (who->GetTypeId() != TYPEID_PLAYER)) + { + if(who->GetEntry() == ILLIDAN_STORMRAGE) // Check if who is Illidan + { + if(!IllidanGUID && m_creature->IsWithinDistInMap(who, 3) && !who->HasAura(SPELL_CAGED, 0)) + { + IllidanGUID = who->GetGUID(); + who->CastSpell(who, SPELL_CAGED, true); + DespawnTimer = 5000; + if(who->HasAura(SPELL_ENRAGE, 0)) + // Dispel his enrage + who->RemoveAurasDueToSpell(SPELL_ENRAGE); + + if(GameObject* CageTrap = GameObject::GetGameObject(*m_creature, CageTrapGUID)) + CageTrap->SetLootState(GO_JUST_DEACTIVATED); + } + } + } + } + + void UpdateAI(const uint32 diff) + { + if(DespawnTimer) + if(DespawnTimer < diff) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + else DespawnTimer -= diff; + + //if(IllidanGUID && !SummonedBeams) + //{ + // if(Unit* Illidan = Unit::GetUnit(*m_creature, IllidanGUID) + // { + // //TODO: Find proper spells and properly apply 'caged' Illidan effect + // } + //} + } +}; + +bool GOHello_cage_trap(Player* plr, GameObject* go) +{ + float x, y, z; + plr->GetPosition(x, y, z); + + Creature* trigger = NULL; + + CellPair pair(MaNGOS::ComputeCellPair(x, y)); + Cell cell(pair); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + // Grid search for nearest live creature of entry 23304 within 10 yards + MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck check(*plr, 23304, true, 10); + MaNGOS::CreatureLastSearcher searcher(trigger, check); + + TypeContainerVisitor, GridTypeMapContainer> cSearcher(searcher); + + CellLock cell_lock(cell, pair); + cell_lock->Visit(cell_lock, cSearcher, *(plr->GetMap())); + + if(!trigger) + { + plr->GetSession()->SendNotification("SD2: Summon failed. This trap is now useless.", LANG_UNIVERSAL, 0); + error_log("SD2: Cage Trap- Unable to find trigger. This Cage Trap is now useless"); + return false; + } + + ((cage_trap_triggerAI*)trigger->AI())->Active = true; + go->SetGoState(0); + return true; +} + +//This is used to sort the players by distance in preparation for being charged by the flames. +struct TargetDistanceOrder : public std::binary_function +{ + 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)); + } +}; + +struct MANGOS_DLL_DECL flame_of_azzinothAI : public ScriptedAI +{ + flame_of_azzinothAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FlameBlastTimer; + uint32 SummonBlazeTimer; + uint32 ChargeTimer; + + void Reset() + { + FlameBlastTimer = 15000 + rand()%15000; + SummonBlazeTimer = 10000 + rand()%20000; + ChargeTimer = 5000; + } + + void Aggro(Unit *who) {} + + void Charge() + { + // Get the Threat List + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + + if(!m_threatlist.size()) return; // He doesn't have anyone in his threatlist, useless to continue + + std::list targets; + std::list::iterator itr = m_threatlist.begin(); + for( ; itr!= m_threatlist.end(); ++itr) //store the threat list in a different container + { + Unit *target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + //only on alive players + if(target && target->isAlive() && target->GetTypeId() == TYPEID_PLAYER ) + targets.push_back( target); + } + + //Sort the list of players + targets.sort(TargetDistanceOrder(m_creature)); + //Resize so we only get the furthest target + targets.resize(1); + + Unit* target = (*targets.begin()); + if(target && (!m_creature->IsWithinDistInMap(target, 40))) + { + DoCast(m_creature, SPELL_ENRAGE, true); + DoCast(target, SPELL_CHARGE); + } + } + + void UpdateAI(const uint32 diff) + { + // Return if we don't have a target + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(FlameBlastTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FLAME_BLAST); + FlameBlastTimer = 30000; + }else FlameBlastTimer -= diff; + + if(SummonBlazeTimer < diff) + { + DoCast(m_creature, SPELL_BLAZE_SUMMON); + SummonBlazeTimer = 30000 + rand()%20000; + }else SummonBlazeTimer -= diff; + + if(ChargeTimer < diff) + { + Charge(); + ChargeTimer = 5000; + }else ChargeTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL shadow_demonAI : public ScriptedAI +{ + shadow_demonAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint64 TargetGUID; + + void Reset() { TargetGUID = 0; } + + void Aggro(Unit *who) {} + + void JustDied(Unit *killer) + { + if(TargetGUID) + { + Unit* target = Unit::GetUnit((*m_creature), TargetGUID); + if(target) + target->RemoveAurasDueToSpell(SPELL_PARALYZE); + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) return; + + // Only cast the below on players. + if(m_creature->getVictim()->GetTypeId() != TYPEID_PLAYER) return; + + if(!m_creature->getVictim()->HasAura(SPELL_PARALYZE, 0)) + { + TargetGUID = m_creature->getVictim()->GetGUID(); + m_creature->AddThreat(m_creature->getVictim(), 10000000.0f); + DoCast(m_creature, SPELL_SHADOW_DEMON_PASSIVE, true); + DoCast(m_creature->getVictim(), SPELL_PURPLE_BEAM, true); + DoCast(m_creature->getVictim(), SPELL_PARALYZE, true); + } + // Kill our target if we're very close. + if(m_creature->IsWithinDistInMap(m_creature->getVictim(), 3)) + DoCast(m_creature->getVictim(), SPELL_CONSUME_SOUL); + } +}; + +struct MANGOS_DLL_DECL flamecrashAI : public ScriptedAI +{ + flamecrashAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FlameCrashTimer; + uint32 DespawnTimer; + + void Reset() + { + FlameCrashTimer = 3000 +rand()%5000; + DespawnTimer = 60000; + } + + void Aggro(Unit *who){ return; } + + void AttackStart(Unit *who) { } + + void MoveInLineOfSight(Unit *who){ } + + void UpdateAI(const uint32 diff) + { + if(FlameCrashTimer < diff) + { + DoCast(m_creature, SPELL_FLAME_CRASH_EFFECT); + FlameCrashTimer = 15000; + }else FlameCrashTimer -= diff; + + if(DespawnTimer < diff) + { + m_creature->SetVisibility(VISIBILITY_OFF); // So that players don't see the sparkly effect when we die. + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + }else DespawnTimer -= diff; + } +}; + +// Shadowfiends interact with Illidan, setting more targets in Illidan's hashmap +struct MANGOS_DLL_SPEC mob_parasitic_shadowfiendAI : public ScriptedAI +{ + mob_parasitic_shadowfiendAI(Creature* c) : ScriptedAI(c) + { + Reset(); + } + + void Reset() {} + + void Aggro(Unit* who) {} + + void DoMeleeAttackIfReady() + { + //If we are within range melee the target + if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + { + //Make sure our attack is ready and we aren't currently casting + if( m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false)) + { + if(!m_creature->getVictim()->HasAura(SPELL_PARASITIC_SHADOWFIEND, 0)) + DoCast(m_creature->getVictim(), SPELL_PARASITIC_SHADOWFIEND, true); + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); + } + } + } +}; + +struct MANGOS_DLL_DECL blazeAI : public ScriptedAI +{ + blazeAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 BlazeTimer; + uint32 DespawnTimer; + + void Reset() + { + BlazeTimer = 2000; + DespawnTimer = 15000; + } + + void Aggro(Unit *who){ } + + void AttackStart(Unit* who) { } + + void MoveInLineOfSight(Unit *who){ } + + void UpdateAI(const uint32 diff) + { + if(BlazeTimer < diff) + { + DoCast(m_creature, SPELL_BLAZE_EFFECT); + BlazeTimer = 15000; + }else BlazeTimer -= diff; + + if(DespawnTimer < diff) + { + m_creature->SetVisibility(VISIBILITY_OFF); + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + }else DespawnTimer -= diff; + } +}; + +struct MANGOS_DLL_DECL blade_of_azzinothAI : public ScriptedAI +{ + blade_of_azzinothAI(Creature* c) : ScriptedAI(c) { Reset(); } + + void Reset() {} + // Do-Nothing-But-Stand-There + void Aggro(Unit* who) { } + void AttackStart(Unit* who) { } + void MoveInLineOfSight(Unit* who) { } + +}; + +CreatureAI* GetAI_boss_illidan_stormrage(Creature *_Creature) +{ + return new boss_illidan_stormrageAI (_Creature); +} + +CreatureAI* GetAI_npc_akama_at_illidan(Creature *_Creature) +{ + npc_akama_illidanAI* Akama_AI = new npc_akama_illidanAI(_Creature); + + for(uint8 i = 0; i < 13; ++i) + Akama_AI->AddWaypoint(i, AkamaWP[i].x, AkamaWP[i].y, AkamaWP[i].z); + + return ((CreatureAI*)Akama_AI); +} + +CreatureAI* GetAI_boss_maiev(Creature *_Creature) +{ + return new boss_maievAI (_Creature); +} + +CreatureAI* GetAI_mob_flame_of_azzinoth(Creature *_Creature) +{ + return new flame_of_azzinothAI (_Creature); +} + +CreatureAI* GetAI_cage_trap_trigger(Creature *_Creature) +{ + return new cage_trap_triggerAI (_Creature); +} + +CreatureAI* GetAI_shadow_demon(Creature *_Creature) +{ + return new shadow_demonAI (_Creature); +} + +CreatureAI* GetAI_flamecrash(Creature *_Creature) +{ + return new flamecrashAI (_Creature); +} + +CreatureAI* GetAI_demonfire(Creature *_Creature) +{ + return new demonfireAI (_Creature); +} + +CreatureAI* GetAI_blaze(Creature *_Creature) +{ + return new blazeAI (_Creature); +} + +CreatureAI* GetAI_blade_of_azzinoth(Creature *_Creature) +{ + return new blade_of_azzinothAI (_Creature); +} + +CreatureAI* GetAI_parasitic_shadowfiend(Creature *_Creature) +{ + return new mob_parasitic_shadowfiendAI (_Creature); +} + +void AddSC_boss_illidan() +{ + Script* newscript; + + newscript = new Script; + newscript->Name = "boss_illidan_stormrage"; + newscript->GetAI = GetAI_boss_illidan_stormrage; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "npc_akama_illidan"; + newscript->GetAI = GetAI_npc_akama_at_illidan; + newscript->pGossipHello = GossipHello_npc_akama_at_illidan; + newscript->pGossipSelect = GossipSelect_npc_akama_at_illidan; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "boss_maiev_shadowsong"; + newscript->GetAI = GetAI_boss_maiev; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_flame_of_azzinoth"; + newscript->GetAI = GetAI_mob_flame_of_azzinoth; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_blade_of_azzinoth"; + newscript->GetAI = GetAI_blade_of_azzinoth; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "gameobject_cage_trap"; + newscript->pGOHello = GOHello_cage_trap; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_cage_trap_trigger"; + newscript->GetAI = GetAI_cage_trap_trigger; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_shadow_demon"; + newscript->GetAI = GetAI_shadow_demon; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_flame_crash"; + newscript->GetAI = GetAI_flamecrash; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_demon_fire"; + newscript->GetAI = GetAI_demonfire; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_blaze"; + newscript->GetAI = GetAI_blaze; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_parasitic_shadowfiend"; + newscript->GetAI = GetAI_parasitic_shadowfiend; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/boss_mother_shahraz.cpp b/src/bindings/scripts/scripts/zone/black_temple/boss_mother_shahraz.cpp new file mode 100644 index 00000000000..383e3b05656 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/boss_mother_shahraz.cpp @@ -0,0 +1,353 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Mother_Shahraz +SD%Complete: 80 +SDComment: Saber Lash missing, Fatal Attraction slightly incorrect; need to damage only if affected players are within range of each other +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "def_black_temple.h" + +//Spells +#define SPELL_BEAM_SINISTER 40859 +#define SPELL_BEAM_VILE 40860 +#define SPELL_BEAM_WICKED 40861 +#define SPELL_BEAM_SINFUL 40827 +#define SPELL_ATTRACTION 40871 +#define SPELL_SILENCING_SHRIEK 40823 +#define SPELL_ENRAGE 23537 +#define SPELL_SABER_LASH 43267 +#define SPELL_SABER_LASH_IMM 43690 +#define SPELL_TELEPORT_VISUAL 40869 +#define SPELL_BERSERK 45078 + +uint32 PrismaticAuras[]= +{ + 40880, // Shadow + 40882, // Fire + 40883, // Nature + 40891, // Arcane + 40896, // Frost + 40897, // Holy +}; + +//Speech'n'Sounds +#define SAY_TAUNT1 "You play, you pay." +#define SOUND_TAUNT1 11501 + +#define SAY_TAUNT2 "I'm not impressed." +#define SOUND_TAUNT2 11502 + +#define SAY_TAUNT3 "Enjoying yourselves?" +#define SOUND_TAUNT3 11503 + +#define SAY_AGGRO "So, business... Or pleasure?" +#define SOUND_AGGRO 11504 + +#define SAY_SPELL1 "You seem a little tense." +#define SOUND_SPELL1 11505 + +#define SAY_SPELL2 "Don't be shy." +#define SOUND_SPELL2 11506 + +#define SAY_SPELL3 "I'm all... yours." +#define SOUND_SPELL3 11507 + +#define SAY_SLAY1 "Easy come, easy go." +#define SOUND_SLAY1 11508 + +#define SAY_SLAY2 "So much for a happy ending." +#define SOUND_SLAY2 11509 + +#define SAY_ENRAGE "Stop toying with my emotions!" +#define SOUND_ENRAGE 11510 + +#define SAY_DEATH "I wasn't... finished." +#define SOUND_DEATH 11511 + +struct Locations +{ + float x,y,z; +}; + +static Locations TeleportPoint[]= +{ + {959.996, 212.576, 193.843}, + {932.537, 231.813, 193.838}, + {958.675, 254.767, 193.822}, + {946.955, 201.316, 192.535}, + {944.294, 149.676, 197.551}, + {930.548, 284.888, 193.367}, + {965.997, 278.398, 195.777} +}; + +struct MANGOS_DLL_DECL boss_shahrazAI : public ScriptedAI +{ + boss_shahrazAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 TargetGUID[3]; + uint32 BeamTimer; + uint32 BeamCount; + uint32 CurrentBeam; + uint32 PrismaticShieldTimer; + uint32 FatalAttractionTimer; + uint32 FatalAttractionExplodeTimer; + uint32 ShriekTimer; + uint32 RandomYellTimer; + uint32 EnrageTimer; + uint32 ExplosionCount; + + bool Enraged; + + void Reset() + { + if(pInstance) + pInstance->SetData(DATA_MOTHERSHAHRAZEVENT, NOT_STARTED); + + for(uint8 i = 0; i<3; i++) + TargetGUID[i] = 0; + + BeamTimer = 60000; // Timers may be incorrect + BeamCount = 0; + CurrentBeam = 0; // 0 - Sinister, 1 - Vile, 2 - Wicked, 3 - Sinful + PrismaticShieldTimer = 0; + FatalAttractionTimer = 60000; + FatalAttractionExplodeTimer = 70000; + ShriekTimer = 30000; + RandomYellTimer = 70000 + rand()%41 * 1000; + EnrageTimer = 600000; + ExplosionCount = 0; + + Enraged = false; + } + + void Aggro(Unit *who) + { + if(pInstance) + pInstance->SetData(DATA_MOTHERSHAHRAZEVENT, IN_PROGRESS); + + DoZoneInCombat(); + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *victim) + { + if(pInstance) + pInstance->SetData(DATA_MOTHERSHAHRAZEVENT, DONE); + + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + void TeleportPlayers() + { + uint32 random = rand()%7; + float X = TeleportPoint[random].x; + float Y = TeleportPoint[random].y; + float Z = TeleportPoint[random].z; + for(uint8 i = 0; i < 3; i++) + { + Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(pUnit && pUnit->isAlive() && (pUnit->GetTypeId() == TYPEID_PLAYER)) + { + TargetGUID[i] = pUnit->GetGUID(); + pUnit->CastSpell(pUnit, SPELL_TELEPORT_VISUAL, true); + DoTeleportPlayer(pUnit, X, Y, Z, pUnit->GetOrientation()); + } + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 10) && !Enraged) + { + Enraged = true; + DoCast(m_creature, SPELL_ENRAGE, true); + DoYell(SAY_ENRAGE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + } + + //Randomly cast one beam. + if(BeamTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(!target || !target->isAlive()) + return; + + BeamTimer = 9000; + + switch(CurrentBeam) + { + case 0: + DoCast(target, SPELL_BEAM_SINISTER); + break; + case 1: + DoCast(target, SPELL_BEAM_VILE); + break; + case 2: + DoCast(target, SPELL_BEAM_WICKED); + break; + case 3: + DoCast(target, SPELL_BEAM_SINFUL); + break; + } + BeamCount++; + uint32 Beam = CurrentBeam; + if(BeamCount > 3) + while(CurrentBeam == Beam) + CurrentBeam = rand()%3; + + }else BeamTimer -= diff; + + // Random Prismatic Shield every 15 seconds. + if(PrismaticShieldTimer < diff) + { + uint32 random = rand()%6; + if(PrismaticAuras[random]) + DoCast(m_creature, PrismaticAuras[random]); + PrismaticShieldTimer = 15000; + }else PrismaticShieldTimer -= diff; + + // Select 3 random targets (can select same target more than once), teleport to a random location then make them cast explosions until they get away from each other. + if(FatalAttractionTimer < diff) + { + ExplosionCount = 0; + + TeleportPlayers(); + + switch(rand()%2) + { + case 0: + DoYell(SAY_SPELL2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SPELL2); + break; + case 1: + DoYell(SAY_SPELL3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SPELL3); + break; + } + FatalAttractionExplodeTimer = 2000; + FatalAttractionTimer = 40000 + rand()%31 * 1000; + }else FatalAttractionTimer -= diff; + + if(FatalAttractionExplodeTimer < diff) + { + // Just make them explode three times... they're supposed to keep exploding while they are in range, but it'll take too much code. I'll try to think of an efficient way for it later. + if(ExplosionCount < 3) + { + for(uint8 i = 0; i < 4; i++) + { + Unit* pUnit = NULL; + if(TargetGUID[i]) + { + pUnit = Unit::GetUnit((*m_creature), TargetGUID[i]); + if(pUnit) + pUnit->CastSpell(pUnit, SPELL_ATTRACTION, true); + TargetGUID[i] = 0; + } + } + + ExplosionCount++; + FatalAttractionExplodeTimer = 1000; + } + else + { + FatalAttractionExplodeTimer = FatalAttractionTimer + 2000; + ExplosionCount = 0; + } + }else FatalAttractionExplodeTimer -= diff; + + if(ShriekTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SILENCING_SHRIEK); + ShriekTimer = 30000; + }else ShriekTimer -= diff; + + //Enrage + if(!m_creature->HasAura(SPELL_BERSERK, 0)) + if(EnrageTimer < diff) + { + DoCast(m_creature, SPELL_BERSERK); + DoYell(SAY_ENRAGE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + }else EnrageTimer -= diff; + + //Random taunts + if(RandomYellTimer < diff) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_TAUNT1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_TAUNT1); + break; + case 1: + DoYell(SAY_TAUNT2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_TAUNT2); + break; + case 2: + DoYell(SAY_TAUNT3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_TAUNT3); + break; + } + RandomYellTimer = 60000 + rand()%91 * 1000; + }else RandomYellTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_shahraz(Creature *_Creature) +{ + return new boss_shahrazAI (_Creature); +} + +void AddSC_boss_mother_shahraz() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_mother_shahraz"; + newscript->GetAI = GetAI_boss_shahraz; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/boss_reliquary_of_souls.cpp b/src/bindings/scripts/scripts/zone/black_temple/boss_reliquary_of_souls.cpp new file mode 100644 index 00000000000..19ba2784880 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/boss_reliquary_of_souls.cpp @@ -0,0 +1,1051 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Reliquary_of_Souls +SD%Complete: 90 +SDComment: Persistent Area Auras for each Essence (Aura of Suffering, Aura of Desire, Aura of Anger) requires core support. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "def_black_temple.h" + +//Sound'n'speech +//Suffering +#define SUFF_SAY_FREED "Pain and suffering are all that await you!" +#define SUFF_SOUND_FREED 11415 +#define SUFF_SAY_AGGRO "Don't leave me alone!" +#define SUFF_SOUND_AGGRO 11416 +#define SUFF_SAY_SLAY1 "Look at what you make me do!" +#define SUFF_SOUND_SLAY1 11417 +#define SUFF_SAY_SLAY2 "I didn't ask for this!" +#define SUFF_SOUND_SLAY2 11418 +#define SUFF_SAY_SLAY3 "The pain is only beginning!" +#define SUFF_SOUND_SLAY3 11419 +#define SUFF_SAY_RECAP "I don't want to go back!" +#define SUFF_SOUND_RECAP 11420 +#define SUFF_SAY_AFTER "Now what do I do?" +#define SUFF_SOUND_AFTER 11421 + +//Desire +#define DESI_SAY_FREED "You can have anything you desire... for a price." +#define DESI_SOUND_FREED 11408 +#define DESI_SAY_SLAY1 "Fulfilment is at hand!" +#define DESI_SOUND_SLAY1 11409 +#define DESI_SAY_SLAY2 "Yes... you'll stay with us now..." +#define DESI_SOUND_SLAY2 11410 +#define DESI_SAY_SLAY3 "Your reach exceeds your grasp." +#define DESI_SOUND_SLAY3 11412 +#define DESI_SAY_SPEC "Be careful what you wish for..." +#define DESI_SOUND_SPEC 11411 +#define DESI_SAY_RECAP "I'll be waiting..." +#define DESI_SOUND_RECAP 11413 +#define DESI_SAY_AFTER "I won't be far..." +#define DESI_SOUND_AFTER 11414 + +//Anger +#define ANGER_SAY_FREED "Beware... I live." +#define ANGER_SOUND_FREED 11399 +#define ANGER_SAY_FREED2 "So... foolish." +#define ANGER_SOUND_FREED2 11400 +#define ANGER_SOUND_SLAY1 11401 +#define ANGER_SAY_SLAY2 "Enough. No more." +#define ANGER_SOUND_SLAY2 11402 +#define ANGER_SAY_SPEC "On your knees!" +#define ANGER_SOUND_SPEC 11403 +#define ANGER_SAY_BEFORE "Beware, coward." +#define ANGER_SOUND_BEFORE 11405 +#define ANGER_SAY_DEATH "I won't... be... ignored." +#define ANGER_SOUND_DEATH 11404 + +//Spells +#define AURA_OF_SUFFERING 41292 +#define AURA_OF_SUFFERING_ARMOR 42017 +#define ESSENCE_OF_SUFFERING_PASSIVE 41296 +#define SPELL_ENRAGE 41305 +#define SPELL_SOUL_DRAIN 41303 +#define SPELL_FIXATE 41295 + +#define AURA_OF_DESIRE 41350 +#define SPELL_RUNE_SHIELD 41431 +#define SPELL_DEADEN 41410 +#define SPELL_SOUL_SHOCK 41426 + +#define AURA_OF_ANGER 41337 +#define SPELL_SELF_SEETHE 41364 +#define SPELL_ENEMY_SEETHE 41520 +#define SPELL_SOUL_SCREAM 41545 +#define SPELL_SPITE 41377 + +#define ENSLAVED_SOUL_PASSIVE 41535 +#define SPELL_SOUL_RELEASE 41542 +#define SPELL_RESTORE_MANA 32848 +#define SPELL_RESTORE_HEALTH 25329 + +#define CREATURE_ENSLAVED_SOUL 23469 + +struct Position +{ + float x,y; +}; + +static Position Coords[]= +{ + {450.4, 212.3}, + {542.1, 212.3}, + {542.1, 168.3}, + {542.1, 137.4}, + {450.4, 137.4}, + {450.4, 168.3} +}; + +struct MANGOS_DLL_DECL npc_enslaved_soulAI : public ScriptedAI +{ + npc_enslaved_soulAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint64 ReliquaryGUID; + + void Reset() + { + ReliquaryGUID = 0; + } + + void Aggro(Unit* who) {} + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if(damage >= m_creature->GetHealth()) + { + if(done_by->GetTypeId() == TYPEID_PLAYER) + { + done_by->CastSpell(done_by, SPELL_RESTORE_HEALTH, true); + if(done_by->GetMaxPower(POWER_MANA) > 0) + { + if((done_by->GetPower(POWER_MANA) / done_by->GetMaxPower(POWER_MANA)) < 70) + { + uint32 mana = done_by->GetPower(POWER_MANA) + (uint32)(done_by->GetMaxPower(POWER_MANA)*0.3); + done_by->SetPower(POWER_MANA, mana); + }else done_by->SetPower(POWER_MANA, done_by->GetMaxPower(POWER_MANA)); + } + } + DoCast(done_by, SPELL_SOUL_RELEASE); + } + } + + void JustDied(Unit *killer); +}; + +struct MANGOS_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI +{ + boss_reliquary_of_soulsAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 SufferingGUID; + uint64 DesireGUID; + uint64 AngerGUID; + + uint32 SoulDeathCount; + // 0 = Out of Combat, 1 = Not started, 2 = Suffering, 3 = Souls, 4 = Desire, 5 = Souls, 6 = Anger + uint32 Phase; + uint32 SummonEssenceTimer; + uint32 DespawnEssenceTimer; + uint32 SoulCount; + uint32 SummonSoulTimer; + uint32 AnimationTimer; + + bool IsDead; + bool EndingPhase; + + void Reset() + { + if(pInstance) + pInstance->SetData(DATA_RELIQUARYOFSOULSEVENT, NOT_STARTED); + + DespawnEssences(); + + SufferingGUID = 0; + DesireGUID = 0; + AngerGUID = 0; + + SoulDeathCount = 0; + Phase = 0; + SummonEssenceTimer = 8000; + DespawnEssenceTimer = 2000; + SoulCount = 0; + SummonSoulTimer = 1000; + AnimationTimer = 8000; + + IsDead = false; + EndingPhase = false; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,0); + m_creature->GetMotionMaster()->Clear(false); + } + + void Aggro(Unit* who) { } + + void AttackStart(Unit* who) { } + + void MoveInLineOfSight(Unit *who) + { + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + if(!InCombat) + { + if(pInstance) + pInstance->SetData(DATA_RELIQUARYOFSOULSEVENT, IN_PROGRESS); + + Phase = 1; + // I R ANNNGRRRY! + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,375); + SummonEssenceTimer = 8000; + AnimationTimer = 5100; + m_creature->AddThreat(who, 1.0f); + + InCombat = true; + } + } + } + } + + void SummonSoul() + { + uint32 random = rand()%6; + float x = Coords[random].x; + float y = Coords[random].y; + Creature* Soul = m_creature->SummonCreature(CREATURE_ENSLAVED_SOUL, x, y, m_creature->GetPositionZ(), m_creature->GetOrientation(), TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000); + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (target && Soul) + { + ((npc_enslaved_soulAI*)Soul->AI())->ReliquaryGUID = m_creature->GetGUID(); + Soul->CastSpell(Soul, ENSLAVED_SOUL_PASSIVE, true); + Soul->AddThreat(target, 1.0f); + SoulCount++; + } + } + + void MergeThreatList(Creature* target) + { + if(!target) return; + + std::list& m_threatlist = target->getThreatManager().getThreatList(); + std::list::iterator itr = m_threatlist.begin(); + for( ; itr != m_threatlist.end(); itr++) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid()); + if(pUnit) + { + m_creature->AddThreat(pUnit, 1.0f); // This is so that we make sure the unit is in Reliquary's threat list before we reset the unit's threat. + m_creature->getThreatManager().modifyThreatPercent(pUnit, -100); + float threat = target->getThreatManager().getThreat(pUnit); + m_creature->AddThreat(pUnit, threat); // This makes it so that the unit has the same amount of threat in Reliquary's threatlist as in the target creature's (One of the Essences). + } + } + } + + void DespawnEssences() + { + // Despawn Essences + Unit* Essence = NULL; + if(SufferingGUID) + Essence = ((Creature*)Unit::GetUnit((*m_creature), SufferingGUID)); + else if(DesireGUID) + Essence = ((Creature*)Unit::GetUnit((*m_creature), DesireGUID)); + else if(AngerGUID) + Essence = ((Creature*)Unit::GetUnit((*m_creature), AngerGUID)); + if(Essence && Essence->isAlive()) + Essence->setDeathState(JUST_DIED); + } + + void JustDied(Unit* killer) + { + if(pInstance) + pInstance->SetData(DATA_RELIQUARYOFSOULSEVENT, DONE); + + InCombat = false; + } + + void UpdateAI(const uint32 diff) + { + if(!Phase) + return; + + // Reset if event is begun and we don't have a threatlist + if(Phase && m_creature->getThreatManager().getThreatList().empty()) + EnterEvadeMode(); + + if(Phase == 1) + { + if(AnimationTimer < diff) + { + // Release the cube + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,374); + AnimationTimer = 8300; + }else AnimationTimer -= diff; + + if(SummonEssenceTimer < diff) + { + // Ribs: open + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,373); + Creature* EssenceSuffering = NULL; + EssenceSuffering = m_creature->SummonCreature(23418, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 1.57, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10000); + + if(EssenceSuffering) + { + EssenceSuffering->Yell(SUFF_SAY_FREED, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(m_creature, SUFF_SOUND_FREED); + Unit* target = SelectUnit(SELECT_TARGET_TOPAGGRO, 0); + if(target) + { + EssenceSuffering->AddThreat(target, 1.0f); + EssenceSuffering->AI()->AttackStart(target); + } + SufferingGUID = EssenceSuffering->GetGUID(); + } + + EndingPhase = false; + Phase = 2; + }else SummonEssenceTimer -= diff; + } + + if(Phase == 2) + { + if(SufferingGUID) + { + Creature* EssenceSuffering = NULL; + EssenceSuffering = ((Creature*)Unit::GetUnit((*m_creature), SufferingGUID)); + + if(!EssenceSuffering || (!EssenceSuffering->isAlive())) + EnterEvadeMode(); + + if(!EndingPhase) + { + if(EssenceSuffering) + { + if(EssenceSuffering->GetHealth() < (EssenceSuffering->GetMaxHealth()*0.1)) + { + MergeThreatList(EssenceSuffering); + EssenceSuffering->RemoveAllAuras(); + EssenceSuffering->DeleteThreatList(); + EssenceSuffering->GetMotionMaster()->MoveFollow(m_creature,0.0f,0.0f); + EssenceSuffering->Yell(SUFF_SAY_RECAP,LANG_UNIVERSAL,0); + DoPlaySoundToSet(m_creature, SUFF_SOUND_RECAP); + EssenceSuffering->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DespawnEssenceTimer = 4000; + AnimationTimer = 2200; + EndingPhase = true; + } + } + } + + if((EndingPhase) && (EssenceSuffering) && (EssenceSuffering->isAlive())) + { + if(AnimationTimer < diff) + { + // Return + EssenceSuffering->SetUInt32Value(UNIT_NPC_EMOTESTATE,374); + AnimationTimer = 10000; + }else AnimationTimer -= diff; + + if(DespawnEssenceTimer < diff) + { + EssenceSuffering->DeleteThreatList(); + EssenceSuffering->Yell(SUFF_SAY_AFTER,LANG_UNIVERSAL,0); + DoPlaySoundToSet(m_creature, SUFF_SOUND_AFTER); + EssenceSuffering->SetUInt32Value(UNIT_FIELD_DISPLAYID, 11686); + EssenceSuffering->setFaction(35); + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,0); + SummonEssenceTimer = 20000; //60000; + AnimationTimer = 18200; //58100; + SoulDeathCount = 0; + SoulCount = 0; + SummonSoulTimer = 1000; + EndingPhase = false; + Phase = 3; + SufferingGUID = 0; + }else DespawnEssenceTimer -= diff; + } + } + } + + if(Phase == 3) + { + if(SoulCount < 36) + { + if(SummonSoulTimer < diff) + { + SummonSoul(); + SummonSoulTimer = 500; + }else SummonSoulTimer -= diff; + } + + if(SoulDeathCount >= SoulCount) + { + if(AnimationTimer < diff) + { + // Release the cube + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,374); + AnimationTimer = 10000; + }else AnimationTimer -= diff; + + if(SummonEssenceTimer < diff) + { + // Ribs: open + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,373); + Creature* EssenceDesire = NULL; + EssenceDesire = m_creature->SummonCreature(23419, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 1.57, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10000); + + if(EssenceDesire) + { + EssenceDesire->Yell(DESI_SAY_FREED, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, DESI_SOUND_FREED); + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + EssenceDesire->AddThreat(target, 1.0f); + EssenceDesire->AI()->AttackStart(target); + } + DesireGUID = EssenceDesire->GetGUID(); + SoulDeathCount = 0; + } + + Phase = 4; + }else SummonEssenceTimer -= diff; + } + } + + if(Phase == 4) + { + if(DesireGUID) + { + Creature* EssenceDesire = NULL; + EssenceDesire = ((Creature*)Unit::GetUnit((*m_creature), DesireGUID)); + + if(!EssenceDesire || !EssenceDesire->isAlive()) + EnterEvadeMode(); + + if(!EndingPhase && EssenceDesire) + { + if(EssenceDesire->GetHealth() < (EssenceDesire->GetMaxHealth()*0.1)) + { + MergeThreatList(EssenceDesire); + EssenceDesire->GetMotionMaster()->MoveFollow(m_creature,0.0f,0.0f); + EssenceDesire->RemoveAllAuras(); + EssenceDesire->DeleteThreatList(); + EssenceDesire->Yell(DESI_SAY_RECAP,LANG_UNIVERSAL,0); + DoPlaySoundToSet(m_creature, DESI_SOUND_RECAP); + EssenceDesire->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DespawnEssenceTimer = 4000; + AnimationTimer = 2200; + EndingPhase = true; + } + } + + if(EndingPhase && EssenceDesire) + { + if(EssenceDesire->isAlive()) + { + if(AnimationTimer < diff) + { + // Return + EssenceDesire->SetUInt32Value(UNIT_NPC_EMOTESTATE,374); + AnimationTimer = 10000; + }else AnimationTimer -= diff; + + if(DespawnEssenceTimer < diff) + { + EssenceDesire->DeleteThreatList(); + EssenceDesire->setFaction(35); + EssenceDesire->Yell(DESI_SAY_AFTER, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(m_creature, DESI_SOUND_AFTER); + EssenceDesire->SetUInt32Value(UNIT_FIELD_DISPLAYID, 11686); + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,0); + SummonEssenceTimer = 20000; + AnimationTimer = 18200; + SoulDeathCount = 0; + SoulCount = 0; + SummonSoulTimer = 1000; + EndingPhase = false; + Phase = 5; + DesireGUID = 0; + }else DespawnEssenceTimer -= diff; + } + } + } + } + + if(Phase == 5) + { + if(SoulCount < 36) + { + if(SummonSoulTimer < diff) + { + SummonSoul(); + SummonSoulTimer = 500; + }else SummonSoulTimer -= diff; + } + + if(SoulDeathCount >= SoulCount) + { + if(AnimationTimer < diff) + { + // Release the cube + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,374); + AnimationTimer = 10000; + }else AnimationTimer -= diff; + + if(SummonEssenceTimer < diff) + { + // Ribs: open + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE,373); + Creature* EssenceAnger = NULL; + EssenceAnger = m_creature->SummonCreature(23420, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 1.57, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 45000); + + if(EssenceAnger) + { + Unit* target = SelectUnit(SELECT_TARGET_TOPAGGRO, 0); + if(target) + { + EssenceAnger->AddThreat(target, 1.0f); + EssenceAnger->AI()->AttackStart(target); + } + AngerGUID = EssenceAnger->GetGUID(); + DoPlaySoundToSet(m_creature, ANGER_SOUND_FREED); + EssenceAnger->Yell(ANGER_SAY_FREED, LANG_UNIVERSAL, 0); + SoulDeathCount = 0; + } + + Phase = 6; + }else SummonEssenceTimer -= diff; + } + } + + if(Phase == 6) + { + if(AngerGUID) + { + Creature* EssenceAnger = NULL; + EssenceAnger = ((Creature*)Unit::GetUnit((*m_creature), AngerGUID)); + + if(!EssenceAnger) + EnterEvadeMode(); + + if(m_creature->isAlive() && EssenceAnger) + { + if(!EssenceAnger->isAlive()) + { + AngerGUID = 0; + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + } + } + } +}; + +//This is used to sort the players by distance in preparation for the Fixate cast. +struct TargetDistanceOrder : public std::binary_function +{ + 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)); + } +}; + +struct MANGOS_DLL_DECL boss_essence_of_sufferingAI : public ScriptedAI +{ + boss_essence_of_sufferingAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint64 StatAuraGUID; + + uint32 AggroYellTimer; + uint32 FixateTimer; + uint32 EnrageTimer; + uint32 SoulDrainTimer; + + void Reset() + { + StatAuraGUID = 0; + + AggroYellTimer = 5000; + FixateTimer = 5000; + EnrageTimer = 30000; + SoulDrainTimer = 150000; + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if((damage >= m_creature->GetHealth()) && (done_by != m_creature)) + { + damage = 0; + // 10% of total health, signalling time to return + m_creature->SetHealth(m_creature->GetMaxHealth()/10); + if(StatAuraGUID) + { + Unit* pUnit = Unit::GetUnit((*m_creature), StatAuraGUID); + if(pUnit) + pUnit->RemoveAurasDueToSpell(AURA_OF_SUFFERING_ARMOR); + } + } + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + DoCast(who, AURA_OF_SUFFERING, true); + DoCast(m_creature, ESSENCE_OF_SUFFERING_PASSIVE, true); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%3) + { + case 0: + DoYell(SUFF_SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SUFF_SOUND_SLAY1); + break; + case 1: + DoYell(SUFF_SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SUFF_SOUND_SLAY2); + break; + case 2: + DoYell(SUFF_SAY_SLAY3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SUFF_SOUND_SLAY3); + break; + } + } + + void JustDied(Unit* killer) + { + } + + void CastFixate() + { + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + if(m_threatlist.empty()) + return; // No point continuing if empty threatlist. + std::list targets; + std::list::iterator itr = m_threatlist.begin(); + for( ; itr != m_threatlist.end(); ++itr) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid()); + // Only alive players + if(pUnit && pUnit->isAlive() && (pUnit->GetTypeId() == TYPEID_PLAYER)) + targets.push_back(pUnit); + } + if(targets.empty()) + return; // No targets added for some reason. No point continuing. + targets.sort(TargetDistanceOrder(m_creature)); // Sort players by distance. + targets.resize(1); // Only need closest target. + Unit* target = targets.front(); // Get the first target. + // Add threat equivalent to threat on victim. + m_creature->AddThreat(target, m_creature->getThreatManager().getThreat(m_creature->getVictim())); + DoCast(target, SPELL_FIXATE); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(m_creature->GetHealth() <= (m_creature->GetMaxHealth()*0.1)) + { + if(StatAuraGUID) + { + Unit* pUnit = NULL; + pUnit = Unit::GetUnit((*m_creature), StatAuraGUID); + if(pUnit) + pUnit->RemoveAurasDueToSpell(AURA_OF_SUFFERING_ARMOR); + } + } + + if(m_creature->GetHealth() <= (m_creature->GetMaxHealth()*0.1)) + { + if(m_creature->getVictim()) + m_creature->DeleteThreatList(); // Delete our threatlist if below 10% as we should no longer attack. + return; + } + + // Prevent overlapping yells + if(AggroYellTimer) + if(AggroYellTimer < diff) + { + DoYell(SUFF_SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SUFF_SOUND_AGGRO); + AggroYellTimer = 0; + }else AggroYellTimer -= diff; + + //Supposed to be cast on nearest target + if(FixateTimer < diff) + { + CastFixate(); + FixateTimer = 5000; + }else FixateTimer -= diff; + + if(EnrageTimer < diff) + { + DoCast(m_creature, SPELL_ENRAGE); + EnrageTimer = 60000; + }else EnrageTimer -= diff; + + if(SoulDrainTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + if(target) + DoCast(target, SPELL_SOUL_DRAIN); + SoulDrainTimer = 60000; + }else SoulDrainTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; +struct MANGOS_DLL_DECL boss_essence_of_desireAI : public ScriptedAI +{ + boss_essence_of_desireAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 AggroYellTimer; + uint32 RuneShieldTimer; + uint32 DeadenTimer; + uint32 SoulShockTimer; + + void Reset() + { + AggroYellTimer = 5000; + RuneShieldTimer = 60000; + DeadenTimer = 15000; + SoulShockTimer = 40000; + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if((damage >= m_creature->GetHealth()) && (done_by != m_creature)) + { + damage = 0; + // 10% of total health, signalling time to return + m_creature->SetHealth(m_creature->GetMaxHealth()/10); + } + else + { + if(done_by && (done_by->GetTypeId() == TYPEID_PLAYER) && done_by->isAlive()) + done_by->DealDamage(done_by, damage/2, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + + void Aggro(Unit *who) { DoZoneInCombat(); } + + void KilledUnit(Unit *victim) + { + switch(rand()%3) + { + case 0: + DoYell(DESI_SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, DESI_SOUND_SLAY1); + break; + case 1: + DoYell(DESI_SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, DESI_SOUND_SLAY2); + break; + case 2: + DoYell(DESI_SAY_SLAY3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, DESI_SOUND_SLAY3); + break; + } + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + //Begin melee attack if we are within range + DoStartAttackAndMovement(who); + + if (!InCombat) + { + DoCast(who, AURA_OF_DESIRE); + } + } + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(m_creature->GetHealth() <= (m_creature->GetMaxHealth()*0.1)) + { + if(m_creature->getVictim()) + m_creature->DeleteThreatList(); // Delete our threatlist if below 10% as we should no longer attack. + return; + } + + if(RuneShieldTimer < diff) + { + DoCast(m_creature, SPELL_RUNE_SHIELD); + RuneShieldTimer = 60000; + }else RuneShieldTimer -= diff; + + if(DeadenTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_DEADEN); + DeadenTimer = 30000 + rand()%30001; + }else DeadenTimer -= diff; + + if(SoulShockTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SOUL_SHOCK); + SoulShockTimer = 40000; + if(rand()%2 == 0) + { + DoYell(DESI_SAY_SPEC,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, DESI_SOUND_SPEC); + } + + }else SoulShockTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_essence_of_angerAI : public ScriptedAI +{ + boss_essence_of_angerAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint64 AggroTargetGUID; + + uint32 AggroYellTimer; + uint32 CheckTankTimer; + uint32 SoulScreamTimer; + uint32 SpiteTimer; + + bool CheckedAggro; + + void Reset() + { + AggroTargetGUID = 0; + + AggroYellTimer = 5000; + CheckTankTimer = 5000; + SoulScreamTimer = 10000; + SpiteTimer = 30000; + + CheckedAggro = false; + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + DoCast(m_creature->getVictim(), AURA_OF_ANGER, true); + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + //Begin melee attack if we are within range + DoStartAttackAndMovement(who); + + if (!InCombat) + { + DoCast(who, AURA_OF_ANGER); + } + } + } + } + + void JustDied(Unit *victim) + { + DoYell(ANGER_SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,ANGER_SOUND_DEATH); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoPlaySoundToSet(m_creature, ANGER_SOUND_SLAY1); + break; + case 1: + DoYell(ANGER_SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, ANGER_SOUND_SLAY2); + break; + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(!CheckedAggro) + { + AggroTargetGUID = m_creature->getVictim()->GetGUID(); + CheckedAggro = true; + } + + if(AggroYellTimer) + if(AggroYellTimer < diff) + { + DoYell(ANGER_SAY_FREED2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, ANGER_SOUND_FREED2); + AggroYellTimer = 0; + }else AggroYellTimer -= diff; + + if(CheckTankTimer < diff) + { + if(m_creature->getVictim()->GetGUID() != AggroTargetGUID) + { + DoYell(ANGER_SAY_BEFORE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, ANGER_SOUND_BEFORE); + DoCast(m_creature, SPELL_SELF_SEETHE); + DoCast(m_creature->getVictim(), SPELL_ENEMY_SEETHE, true); + AggroTargetGUID = m_creature->getVictim()->GetGUID(); + } + CheckTankTimer = 2000; + }else CheckTankTimer -= diff; + + if(SoulScreamTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SOUL_SCREAM); + SoulScreamTimer = 10000; + }else SoulScreamTimer -= diff; + + if(SpiteTimer < diff) + { + for(uint8 i = 0; i < 4; i++) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + if(target) + DoCast(target, SPELL_SPITE); + + } + + SpiteTimer = 30000; + DoYell(ANGER_SAY_SPEC,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, ANGER_SOUND_SPEC); + }else SpiteTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +void npc_enslaved_soulAI::JustDied(Unit *killer) +{ + if(ReliquaryGUID) + { + Creature* Reliquary = ((Creature*)Unit::GetUnit((*m_creature), ReliquaryGUID)); + if(Reliquary) + ((boss_reliquary_of_soulsAI*)Reliquary->AI())->SoulDeathCount++; + } +} + +CreatureAI* GetAI_boss_reliquary_of_souls(Creature *_Creature) +{ + return new boss_reliquary_of_soulsAI (_Creature); +} + +CreatureAI* GetAI_boss_essence_of_suffering(Creature *_Creature) +{ + return new boss_essence_of_sufferingAI (_Creature); +} + +CreatureAI* GetAI_boss_essence_of_desire(Creature *_Creature) +{ + return new boss_essence_of_desireAI (_Creature); +} + +CreatureAI* GetAI_boss_essence_of_anger(Creature *_Creature) +{ + return new boss_essence_of_angerAI (_Creature); +} + +CreatureAI* GetAI_npc_enslaved_soul(Creature *_Creature) +{ + return new npc_enslaved_soulAI (_Creature); +} + +void AddSC_boss_reliquary_of_souls() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_reliquary_of_souls"; + newscript->GetAI = GetAI_boss_reliquary_of_souls; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_essence_of_suffering"; + newscript->GetAI = GetAI_boss_essence_of_suffering; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_essence_of_desire"; + newscript->GetAI = GetAI_boss_essence_of_desire; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_essence_of_anger"; + newscript->GetAI = GetAI_boss_essence_of_anger; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_enslaved_soul"; + newscript->GetAI = GetAI_npc_enslaved_soul; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/boss_shade_of_akama.cpp b/src/bindings/scripts/scripts/zone/black_temple/boss_shade_of_akama.cpp new file mode 100644 index 00000000000..2f1034e2c08 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/boss_shade_of_akama.cpp @@ -0,0 +1,813 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Shade_of_Akama +SD%Complete: 99 +SDComment: Seems to be complete. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "def_black_temple.h" +#include "sc_grid_searchers.h" + +struct Location +{ + float x, y, o, z; +}; + +static Location ChannelerLocations[]= +{ + {463.161285, 401.219757, 3.141592}, + {457.377625, 391.227661, 2.106461}, + {446.012421, 391.227661, 1.071904}, + {439.533783, 401.219757, 0.000000}, + {446.012421, 411.211853, 5.210546}, + {457.377625, 411.211853, 4.177494} +}; + +static Location SpawnLocations[]= +{ + {498.652740, 461.728119, 0}, + {498.505003, 339.619324, 0} +}; + +static Location AkamaWP[]= +{ + {482.352448, 401.162720, 0, 112.783928}, + {469.597443, 402.264404, 0, 118.537910} +}; + +static Location BrokenCoords[]= +{ + {541.375916, 401.439575, M_PI, 112.783997}, // The place where Akama channels + {534.130005, 352.394531, 2.164150, 112.783737}, // Behind a 'pillar' which is behind the east alcove + {499.621185, 341.534729, 1.652856, 112.783730}, // East Alcove + {499.151093, 461.036438, 4.770888, 112.78370}, // West Alcove +}; + +static Location BrokenWP[]= +{ + {492.491638, 400.744690, 3.122336, 112.783737}, + {494.335724, 382.221771, 2.676230, 112.783737}, + {489.555939, 373.507202, 2.416263, 112.783737}, + {491.136353, 427.868774, 3.519748, 112.783737}, +}; + +// Locations +#define Z1 118.543144 +#define Z2 120.783768 +#define Z_SPAWN 113.537949 +#define AGGRO_X 482.793182 +#define AGGRO_Y 401.270172 +#define AGGRO_Z 112.783928 +#define AKAMA_X 514.583984 +#define AKAMA_Y 400.601013 +#define AKAMA_Z 112.783997 + +// Texts +#define SOUND_DEATH 11386 +#define SAY_DEATH "No! Not yet..." +#define SOUND_LOW_HEALTH 11385 +#define SAY_LOW_HEALTH "I will not last much longer..." + +// Ending cinematic text +#define SAY_FREE "Come out from the shadows! I've returned to lead you against our true enemy! Shed your chains and raise your weapons against your Illidari masters!" +#define SAY_BROKEN_FREE_01 "Hail our leader! Hail Akama!" +#define SAY_BROKEN_FREE_02 "Hail Akama!" + +// Gossips +#define GOSSIP_ITEM "We are ready to fight alongside you, Akama" + +// Spells +#define SPELL_VERTEX_SHADE_BLACK 39833 +#define SPELL_SHADE_SOUL_CHANNEL 40401 +#define SPELL_DESTRUCTIVE_POISON 40874 +#define SPELL_LIGHTNING_BOLT 42024 +#define SPELL_AKAMA_SOUL_CHANNEL 40447 +#define SPELL_AKAMA_SOUL_RETRIEVE 40902 +#define AKAMA_SOUL_EXPEL 40855 +#define SPELL_SHADE_SOUL_CHANNEL_2 40520 + +// Channeler entry +#define CREATURE_CHANNELER 23421 +#define CREATURE_SORCERER 23215 +#define CREATURE_DEFENDER 23216 +#define CREATURE_BROKEN 23319 + +const uint32 spawnEntries[4]= { 23523, 23318, 23524 }; + +struct MANGOS_DLL_DECL mob_ashtongue_channelerAI : public ScriptedAI +{ + mob_ashtongue_channelerAI(Creature* c) : ScriptedAI(c) { Reset(); } + + uint64 ShadeGUID; + + void Reset() { ShadeGUID = 0; } + void JustDied(Unit* killer); + void Aggro(Unit* who) {} + void AttackStart(Unit* who) {} + void MoveInLineOfSight(Unit* who) {} + void UpdateAI(const uint32 diff) {} +}; + +struct MANGOS_DLL_DECL mob_ashtongue_sorcererAI : public ScriptedAI +{ + mob_ashtongue_sorcererAI(Creature* c) : ScriptedAI(c) { Reset(); } + + uint64 ShadeGUID; + uint32 CheckTimer; + bool StartBanishing; + + void Reset() + { + StartBanishing = false; + CheckTimer = 5000; + ShadeGUID = 0; + } + + void JustDied(Unit* killer); + void Aggro(Unit* who) {} + void AttackStart(Unit* who) {} + void MoveInLineOfSight(Unit* who) {} + void UpdateAI(const uint32 diff) + { + if(StartBanishing) + return; + + if(CheckTimer < diff) + { + Unit* Shade = Unit::GetUnit((*m_creature), ShadeGUID); + if(Shade && Shade->isAlive() && m_creature->isAlive()) + { + if(m_creature->GetDistance2d(Shade) < 20) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + DoCast(Shade, SPELL_SHADE_SOUL_CHANNEL, true); + DoCast(Shade, SPELL_SHADE_SOUL_CHANNEL_2, true); + + StartBanishing = true; + } + } + CheckTimer = 2000; + }else CheckTimer -= diff; + } +}; + +struct MANGOS_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI +{ + boss_shade_of_akamaAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + AkamaGUID = pInstance ? pInstance->GetData64(DATA_AKAMA_SHADE) : 0; + Reset(); + } + + ScriptedInstance* pInstance; + + std::list Channelers; + std::list Sorcerers; + uint64 AkamaGUID; + + uint32 SorcererCount; + uint32 DeathCount; + + uint32 ReduceHealthTimer; + uint32 SummonTimer; + uint32 ResetTimer; + uint32 DefenderTimer; // They are on a flat 15 second timer, independant of the other summon creature timer. + + bool IsBanished; + bool HasKilledAkama; + + void Reset() + { + FindChannelers(); + + if(!Channelers.empty()) + for(std::list::iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) + { + Creature* Channeler = NULL; + Channeler = ((Creature*)Unit::GetUnit(*m_creature, *itr)); + if(Channeler) + { + if(Channeler->isDead()) + { + Channeler->RemoveCorpse(); + Channeler->Respawn(); + } + Channeler->CastSpell(m_creature, SPELL_SHADE_SOUL_CHANNEL, true); + Channeler->CastSpell(m_creature, SPELL_SHADE_SOUL_CHANNEL_2, true); + Channeler->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + else error_log("SD2 ERROR: No Channelers are stored in the list. This encounter will not work properly"); + + if(!Sorcerers.empty()) + for(std::list::iterator itr = Sorcerers.begin(); itr != Sorcerers.end(); ++itr) + if(Creature* Sorcerer = ((Creature*)Unit::GetUnit(*m_creature, *itr))) + if(Sorcerer->isAlive()) + { + Sorcerer->SetVisibility(VISIBILITY_OFF); + Sorcerer->DealDamage(Sorcerer, Sorcerer->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + Sorcerers.clear(); + + if(Unit* Akama = Unit::GetUnit(*m_creature, AkamaGUID)) + Akama->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + SorcererCount = 0; + DeathCount = 0; + + SummonTimer = 10000; + ReduceHealthTimer = 0; + ResetTimer = 60000; + DefenderTimer = 15000; + + IsBanished = true; + HasKilledAkama = false; + + m_creature->SetVisibility(VISIBILITY_ON); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN); + + if(pInstance && m_creature->isAlive()) + pInstance->SetData(DATA_SHADEOFAKAMAEVENT, NOT_STARTED); + } + + void Aggro(Unit* who) { } + + void AttackStart(Unit* who) + { + if(!who || IsBanished) return; + + if(who->isTargetableForAttack() && who != m_creature) + DoStartAttackAndMovement(who); + } + + void MoveInLineOfSight(Unit* who) + { + if(IsBanished) return; + + ScriptedAI::MoveInLineOfSight(who); + } + + void IncrementDeathCount(uint64 guid = 0) // If guid is set, will remove it from list of sorcerer + { + debug_log("SD2: Increasing Death Count for Shade of Akama encounter"); + ++DeathCount; + m_creature->RemoveSingleAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2, 0); + if(guid) + { + if(Sorcerers.empty()) + error_log("SD2 ERROR: Shade of Akama - attempt to remove guid %u from Sorcerers list but list is already empty", guid); + else Sorcerers.remove(guid); + } + } + + void SummonCreature() + { + uint32 random = rand()%2; + float X = SpawnLocations[random].x; + float Y = SpawnLocations[random].y; + // max of 6 sorcerers can be summoned + if((rand()%3 == 0) && (DeathCount > 0) && (SorcererCount < 7)) + { + Creature* Sorcerer = m_creature->SummonCreature(CREATURE_SORCERER, X, Y, Z_SPAWN, 0, TEMPSUMMON_DEAD_DESPAWN, 0); + if(Sorcerer) + { + ((mob_ashtongue_sorcererAI*)Sorcerer->AI())->ShadeGUID = m_creature->GetGUID(); + Sorcerer->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + Sorcerer->GetMotionMaster()->MovePoint(0, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + Sorcerer->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->GetGUID()); + Sorcerers.push_back(Sorcerer->GetGUID()); + --DeathCount; + ++SorcererCount; + } + } + else + { + for(uint8 i = 0; i < 3; ++i) + { + Creature* Spawn = m_creature->SummonCreature(spawnEntries[i], X, Y, Z_SPAWN, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 25000); + if(Spawn) + { + Spawn->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + Spawn->GetMotionMaster()->MovePoint(0, AGGRO_X, AGGRO_Y, AGGRO_Z); + } + } + } + } + + void FindChannelers() + { + CellPair pair(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY())); + Cell cell(pair); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + std::list ChannelerList; + + AllCreaturesOfEntryInRange check(m_creature, CREATURE_CHANNELER, 50); + MaNGOS::CreatureListSearcher searcher(ChannelerList, check); + TypeContainerVisitor, GridTypeMapContainer> visitor(searcher); + + CellLock cell_lock(cell, pair); + cell_lock->Visit(cell_lock, visitor, *(m_creature->GetMap())); + + if(!ChannelerList.empty()) + { + Channelers.clear(); + for(std::list::iterator itr = ChannelerList.begin(); itr != ChannelerList.end(); ++itr) + { + ((mob_ashtongue_channelerAI*)(*itr)->AI())->ShadeGUID = m_creature->GetGUID(); + Channelers.push_back((*itr)->GetGUID()); + debug_log("SD2: Shade of Akama Grid Search found channeler %u. Adding to list", (*itr)->GetGUID()); + } + } + else error_log("SD2 ERROR: Grid Search was unable to find any channelers. Shade of Akama encounter will be buggy"); + } + + void SetSelectableChannelers() + { + if(Channelers.empty()) + { + error_log("SD2 ERROR: Channeler List is empty, Shade of Akama encounter will be buggy"); + return; + } + + for(std::list::iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) + if(Creature* Channeler = ((Creature*)Unit::GetUnit(*m_creature, *itr))) + Channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void SetAkamaGUID(uint64 guid) { AkamaGUID = guid; } + + void UpdateAI(const uint32 diff) + { + if(!InCombat) + return; + + if(IsBanished) + { + // Akama is set in the threatlist so when we reset, we make sure that he is not included in our check + if(m_creature->getThreatManager().getThreatList().size() < 2) + EnterEvadeMode(); + + if(DefenderTimer < diff) + { + uint32 ran = rand()%2; + Creature* Defender = m_creature->SummonCreature(CREATURE_DEFENDER, SpawnLocations[ran].x, SpawnLocations[ran].y, Z_SPAWN, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 25000); + if(Defender) + { + Defender->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + bool move = true; + if(AkamaGUID) + { + if(Unit* Akama = Unit::GetUnit(*m_creature, AkamaGUID)) + { + float x, y, z; + Akama->GetPosition(x,y,z); + // They move towards AKama + Defender->GetMotionMaster()->MovePoint(0, x, y, z); + }else move = false; + }else move = false; + if(!move) + Defender->GetMotionMaster()->MovePoint(0, AKAMA_X, AKAMA_Y, AKAMA_Z); + } + DefenderTimer = 15000; + }else DefenderTimer -= diff; + + if(SummonTimer < diff) + { + SummonCreature(); + SummonTimer = 35000; + }else SummonTimer -= diff; + + if(DeathCount >= 6) + { + if(AkamaGUID) + { + Unit* Akama = Unit::GetUnit((*m_creature), AkamaGUID); + if(Akama && Akama->isAlive()) + { + IsBanished = false; + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveChase(Akama); + Akama->GetMotionMaster()->Clear(); + // Shade should move to Akama, not the other way around + Akama->GetMotionMaster()->MoveIdle(); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + // Crazy amount of threat + m_creature->AddThreat(Akama, 10000000.0f); + Akama->AddThreat(m_creature, 10000000.0f); + m_creature->Attack(Akama, true); + Akama->Attack(m_creature, true); + } + } + } + } + else // No longer banished, let's fight Akama now + { + if(ReduceHealthTimer < diff) + { + if(AkamaGUID) + { + Unit* Akama = Unit::GetUnit((*m_creature), AkamaGUID); + if(Akama && Akama->isAlive()) + { + //10 % less health every few seconds. + m_creature->DealDamage(Akama, Akama->GetMaxHealth()/10, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + ReduceHealthTimer = 12000; + } + else + { + HasKilledAkama = true; // Akama is dead or missing, we stop fighting and disappear + m_creature->SetVisibility(VISIBILITY_OFF); + m_creature->SetHealth(m_creature->GetMaxHealth()); + m_creature->RemoveAllAuras(); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + }else ReduceHealthTimer -= diff; + + if(HasKilledAkama) + { + if(ResetTimer < diff) + { + InCombat = false; + EnterEvadeMode(); // Reset a little while after killing Akama + } + else ResetTimer -= diff; + } + + DoMeleeAttackIfReady(); + } + } +}; + +void mob_ashtongue_channelerAI::JustDied(Unit* killer) +{ + Creature* Shade = ((Creature*)Unit::GetUnit((*m_creature), ShadeGUID)); + if(Shade && Shade->isAlive()) + ((boss_shade_of_akamaAI*)Shade->AI())->IncrementDeathCount(); + else error_log("SD2 ERROR: Channeler dead but unable to increment DeathCount for Shade of Akama."); +} + +void mob_ashtongue_sorcererAI::JustDied(Unit* killer) +{ + Creature* Shade = ((Creature*)Unit::GetUnit((*m_creature), ShadeGUID)); + if(Shade && Shade->isAlive()) + ((boss_shade_of_akamaAI*)Shade->AI())->IncrementDeathCount(m_creature->GetGUID()); + else error_log("SD2 ERROR: Sorcerer dead but unable to increment DeathCount for Shade of Akama."); +} + +struct MANGOS_DLL_DECL npc_akamaAI : public ScriptedAI +{ + npc_akamaAI(Creature* c) : ScriptedAI(c) + { + ShadeHasDied = false; + StartCombat = false; + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + ShadeGUID = pInstance ? pInstance->GetData64(DATA_SHADEOFAKAMA) : 0; + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 ShadeGUID; + + uint32 DestructivePoisonTimer; + uint32 LightningBoltTimer; + uint32 CheckTimer; + uint32 CastSoulRetrieveTimer; + uint32 SoulRetrieveTimer; + uint32 SummonBrokenTimer; + uint32 EndingTalkCount; + uint32 WayPointId; + uint32 BrokenSummonIndex; + + std::list BrokenList; + + bool EventBegun; + bool ShadeHasDied; + bool StartCombat; + bool HasYelledOnce; + + void Reset() + { + DestructivePoisonTimer = 15000; + LightningBoltTimer = 10000; + CheckTimer = 2000; + CastSoulRetrieveTimer = 0; + SoulRetrieveTimer = 0; + SummonBrokenTimer = 0; + EndingTalkCount = 0; + WayPointId = 0; + BrokenSummonIndex = 0; + + BrokenList.clear(); + + EventBegun = false; + HasYelledOnce = false; + + m_creature->SetUInt32Value(UNIT_NPC_FLAGS, 0); // Database sometimes has very very strange values + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + + void Aggro(Unit* who) {} + + void BeginEvent(Player* pl) + { + if(!pInstance) + return; + + ShadeGUID = pInstance->GetData64(DATA_SHADEOFAKAMA); + if(!ShadeGUID) + return; + + Creature* Shade = ((Creature*)Unit::GetUnit((*m_creature), ShadeGUID)); + if(Shade) + { + pInstance->SetData(DATA_SHADEOFAKAMAEVENT, IN_PROGRESS); + // Prevent players from trying to restart event + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + ((boss_shade_of_akamaAI*)Shade->AI())->SetAkamaGUID(m_creature->GetGUID()); + ((boss_shade_of_akamaAI*)Shade->AI())->SetSelectableChannelers(); + ((boss_shade_of_akamaAI*)Shade->AI())->InCombat = true; + Shade->AddThreat(m_creature, 1000000.0f); + Shade->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); + Shade->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->GetGUID()); + if(pl) Shade->AddThreat(pl, 1.0f); + DoZoneInCombat(Shade); + EventBegun = true; + } + } + + void MovementInform(uint32 type, uint32 id) + { + if(type != POINT_MOTION_TYPE) + return; + + switch(id) + { + case 0: ++WayPointId; break; + + case 1: + if(Unit* Shade = Unit::GetUnit(*m_creature, ShadeGUID)) + { + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, ShadeGUID); + DoCast(Shade, SPELL_AKAMA_SOUL_RETRIEVE); + EndingTalkCount = 0; + SoulRetrieveTimer = 16000; + } + break; + } + } + + void JustDied(Unit* killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void UpdateAI(const uint32 diff) + { + if(!EventBegun) + return; + + if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 15 && !HasYelledOnce) + { + DoYell(SAY_LOW_HEALTH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_LOW_HEALTH); + HasYelledOnce = true; + } + + if(ShadeGUID && !StartCombat) + { + Creature* Shade = ((Creature*)Unit::GetUnit((*m_creature), ShadeGUID)); + if(Shade && Shade->isAlive()) + { + if(((boss_shade_of_akamaAI*)Shade->AI())->IsBanished) + { + if(CastSoulRetrieveTimer < diff) + { + DoCast(Shade, SPELL_AKAMA_SOUL_CHANNEL); + CastSoulRetrieveTimer = 500; + }else CastSoulRetrieveTimer -= diff; + } + else + { + m_creature->InterruptNonMeleeSpells(false); + StartCombat = true; + } + } + } + + if(ShadeHasDied && (WayPointId == 1)) + { + if(pInstance) pInstance->SetData(DATA_SHADEOFAKAMAEVENT, DONE); + m_creature->GetMotionMaster()->MovePoint(WayPointId, AkamaWP[1].x, AkamaWP[1].y, AkamaWP[1].z); + ++WayPointId; + } + + if(!ShadeHasDied && StartCombat) + { + if(CheckTimer < diff) + { + if(ShadeGUID) + { + Unit* Shade = Unit::GetUnit((*m_creature), ShadeGUID); + if(Shade && !Shade->isAlive()) + { + ShadeHasDied = true; + WayPointId = 0; + m_creature->SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE); + m_creature->GetMotionMaster()->MovePoint(WayPointId, AkamaWP[0].x, AkamaWP[0].y, AkamaWP[0].z); + } + } + CheckTimer = 5000; + }else CheckTimer -= diff; + } + + if(SummonBrokenTimer && BrokenSummonIndex < 4) + if(SummonBrokenTimer <= diff) + { + for(uint8 i = 0; i < 4; ++i) + { + float x = BrokenCoords[BrokenSummonIndex].x + (i*5); + float y = BrokenCoords[BrokenSummonIndex].y + (1*5); + float z = BrokenCoords[BrokenSummonIndex].z; + float o = BrokenCoords[BrokenSummonIndex].o; + Creature* Broken = m_creature->SummonCreature(CREATURE_BROKEN, x, y, z, o, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 360000); + if(Broken) + { + float wx = BrokenWP[BrokenSummonIndex].x + (i*5); + float wy = BrokenWP[BrokenSummonIndex].y + (i*5); + float wz = BrokenWP[BrokenSummonIndex].z; + Broken->GetMotionMaster()->MovePoint(0, wx, wy, wz); + Broken->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + BrokenList.push_back(Broken->GetGUID()); + } + } + ++BrokenSummonIndex; + SummonBrokenTimer = 1000; + }else SummonBrokenTimer -= diff; + + if(SoulRetrieveTimer) + if(SoulRetrieveTimer <= diff) + { + switch(EndingTalkCount) + { + case 0: + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + ++EndingTalkCount; + SoulRetrieveTimer = 2000; + SummonBrokenTimer = 1; + case 1: + DoYell(SAY_FREE, LANG_UNIVERSAL, NULL); + ++EndingTalkCount; + SoulRetrieveTimer = 25000; + break; + case 2: + if(!BrokenList.empty()) + { + bool Yelled = false; + for(std::list::iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr) + if(Unit* pUnit = Unit::GetUnit(*m_creature, *itr)) + { + if(!Yelled) + { + pUnit->MonsterYell(SAY_BROKEN_FREE_01, LANG_UNIVERSAL, 0); + Yelled = true; + } + pUnit->HandleEmoteCommand(EMOTE_ONESHOT_KNEEL); + } + } + ++EndingTalkCount; + SoulRetrieveTimer = 1500; + break; + case 3: + if(!BrokenList.empty()) + for(std::list::iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr) + if(Unit* pUnit = Unit::GetUnit(*m_creature, *itr)) + // This is the incorrect spell, but can't seem to find the right one. + pUnit->CastSpell(pUnit, 39656, true); + ++EndingTalkCount; + SoulRetrieveTimer = 5000; + case 4: + if(!BrokenList.empty()) + for(std::list::iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr) + if(Unit* pUnit = Unit::GetUnit((*m_creature), *itr)) + pUnit->MonsterYell(SAY_BROKEN_FREE_02, LANG_UNIVERSAL, 0); + SoulRetrieveTimer = 0; + break; + } + }else SoulRetrieveTimer -= diff; + + if(!m_creature->getVictim() || !m_creature->SelectHostilTarget()) + return; + + if(DestructivePoisonTimer < diff) + { + // SPELL_DESTRUCTIVE_POISON is self-cast only for some reason so we make our target cast it on itself + m_creature->getVictim()->CastSpell(m_creature->getVictim(), SPELL_DESTRUCTIVE_POISON, true); + DestructivePoisonTimer = 15000; + }else DestructivePoisonTimer -= diff; + + if(LightningBoltTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_LIGHTNING_BOLT); + LightningBoltTimer = 10000; + }else LightningBoltTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_shade_of_akama(Creature *_Creature) +{ + return new boss_shade_of_akamaAI (_Creature); +} + +CreatureAI* GetAI_mob_ashtongue_channeler(Creature *_Creature) +{ + return new mob_ashtongue_channelerAI (_Creature); +} + +CreatureAI* GetAI_mob_ashtongue_sorcerer(Creature *_Creature) +{ + return new mob_ashtongue_sorcererAI (_Creature); +} + +CreatureAI* GetAI_npc_akama_shade(Creature *_Creature) +{ + return new npc_akamaAI (_Creature); +} + +bool GossipSelect_npc_akama(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) //Fight time + { + player->CLOSE_GOSSIP_MENU(); + ((npc_akamaAI*)_Creature->AI())->BeginEvent(player); + } + + return true; +} + +bool GossipHello_npc_akama(Player *player, Creature *_Creature) +{ + if(player->isAlive()) + { + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(907, _Creature->GetGUID()); + } + + return true; +} + +void AddSC_boss_shade_of_akama() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_shade_of_akama"; + newscript->GetAI = GetAI_boss_shade_of_akama; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_ashtongue_channeler"; + newscript->GetAI = GetAI_mob_ashtongue_channeler; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_ashtongue_sorcerer"; + newscript->GetAI = GetAI_mob_ashtongue_sorcerer; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_akama_shade"; + newscript->GetAI = GetAI_npc_akama_shade; + newscript->pGossipHello = &GossipHello_npc_akama; + newscript->pGossipSelect = &GossipSelect_npc_akama; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/boss_supremus.cpp b/src/bindings/scripts/scripts/zone/black_temple/boss_supremus.cpp new file mode 100644 index 00000000000..f3b06ea6940 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/boss_supremus.cpp @@ -0,0 +1,406 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Supremus +SD%Complete: 95 +SDComment: Need to implement doors. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "def_black_temple.h" + +//Spells +#define SPELL_HURTFUL_STRIKE 41926 +#define SPELL_DEMON_FIRE 40029 +#define SPELL_MOLTEN_FLAME 40253 +#define SPELL_VOLCANIC_ERUPTION 40276 +#define SPELL_VOLCANIC_FIREBALL 40118 +#define SPELL_VOLCANIC_GEYSER 42055 +#define SPELL_MOLTEN_PUNCH 40126 +#define SPELL_BERSERK 45078 + +#define CREATURE_VOLCANO 23085 +#define CREATURE_STALKER 23095 + +struct MANGOS_DLL_DECL molten_flameAI : public ScriptedAI +{ + molten_flameAI(Creature *c) : ScriptedAI(c) + { + Reset(); + } + + uint64 SupremusGUID; + bool TargetLocked; + uint32 CheckTimer; + + void Reset() + { + SupremusGUID = 0; + TargetLocked = false; + + CheckTimer = 1000; + } + + void Aggro(Unit *who) {} + void AttackStart(Unit* who) {} + void MoveInLineOfSight(Unit *who) + { + if(TargetLocked) + return; // stop it from aggroing players who move in LOS if we have a target. + if(who && (who != m_creature) && (m_creature->IsWithinDistInMap(who, 10))) + StalkTarget(who); + } + + void SetSupremusGUID(uint64 GUID) { SupremusGUID = GUID; } + + void StalkTarget(Unit* target) + { + if(!target) return; + + m_creature->AddThreat(target, 50000000.0f); + m_creature->GetMotionMaster()->MoveChase(target); + DoCast(m_creature, SPELL_DEMON_FIRE, true); + // DoCast(m_creature, SPELL_MOLTEN_FLAME, true); // This spell damages self, so disabled for now + TargetLocked = true; + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget()) + return; + + if(m_creature->getVictim() && m_creature->isAlive()) + { + if(CheckTimer < diff) + { + if(SupremusGUID) + { + Unit* Supremus = NULL; + Supremus = Unit::GetUnit((*m_creature), SupremusGUID); + if(Supremus && (!Supremus->isAlive())) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), 0, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + CheckTimer = 2000; + }else CheckTimer -= diff; + } + } +}; + +struct MANGOS_DLL_DECL npc_volcanoAI : public ScriptedAI +{ + npc_volcanoAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 CheckTimer; + uint64 SupremusGUID; + uint32 FireballTimer; + uint32 GeyserTimer; + + void Reset() + { + CheckTimer = 1000; + SupremusGUID = 0; + FireballTimer = 500; + GeyserTimer = 0; + } + + void Aggro(Unit *who) {} + void AttackStart(Unit* who) {} + void MoveInLineOfSight(Unit* who) {} + void SetSupremusGUID(uint64 guid) { SupremusGUID = guid; } + + void UpdateAI(const uint32 diff) + { + if(CheckTimer < diff) + { + if(SupremusGUID) + { + Unit* Supremus = NULL; + Supremus = Unit::GetUnit((*m_creature), SupremusGUID); + if(Supremus && (!Supremus->isAlive())) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), 0, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + CheckTimer = 2000; + }else CheckTimer -= diff; + + if(GeyserTimer < diff) + { + DoCast(m_creature, SPELL_VOLCANIC_GEYSER); + GeyserTimer = 18000; + }else GeyserTimer -= diff; + } +}; + +struct MANGOS_DLL_DECL boss_supremusAI : public ScriptedAI +{ + boss_supremusAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 SummonFlameTimer; + uint32 SwitchTargetTimer; + uint32 PhaseSwitchTimer; + uint32 SummonVolcanoTimer; + uint32 HurtfulStrikeTimer; + uint32 BerserkTimer; + + bool Phase1; + + void Reset() + { + if(pInstance) + { + if(m_creature->isAlive()) + { + pInstance->SetData(DATA_SUPREMUSEVENT, NOT_STARTED); + ToggleDoors(true); + } + else ToggleDoors(false); + } + + HurtfulStrikeTimer = 5000; + SummonFlameTimer = 20000; + SwitchTargetTimer = 90000; + PhaseSwitchTimer = 60000; + SummonVolcanoTimer = 5000; + BerserkTimer = 900000; // 15 minute enrage + + Phase1 = true; + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + + if(pInstance) + pInstance->SetData(DATA_SUPREMUSEVENT, IN_PROGRESS); + } + + void ToggleDoors(bool close) + { + if(GameObject* Doors = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_GAMEOBJECT_SUPREMUS_DOORS))) + { + if(close) Doors->SetGoState(1); // Closed + else Doors->SetGoState(0); // Open + } + } + + void JustDied(Unit *killer) + { + if(pInstance) + { + pInstance->SetData(DATA_SUPREMUSEVENT, DONE); + ToggleDoors(false); + } + } + + float CalculateRandomCoord(float initial) + { + float coord = 0; + + switch(rand()%2) + { + case 0: coord = initial + 20 + rand()%20; break; + case 1: coord = initial - 20 - rand()%20; break; + } + + return coord; + } + + Creature* SummonCreature(uint32 entry, Unit* target) + { + if(target && entry) + { + Creature* Summon = m_creature->SummonCreature(entry, CalculateRandomCoord(target->GetPositionX()), CalculateRandomCoord(target->GetPositionY()), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 20000); + if(Summon) + { + Summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Summon->setFaction(m_creature->getFaction()); + return Summon; + } + } + return NULL; + } + + Unit* CalculateHurtfulStrikeTarget() + { + uint32 health = 0; + Unit* target = NULL; + + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + std::list::iterator i = m_threatlist.begin(); + for (i = m_threatlist.begin(); i!= m_threatlist.end();++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit && m_creature->IsWithinDistInMap(pUnit, ATTACK_DISTANCE)) + { + if(pUnit->GetHealth() > health) + { + health = pUnit->GetHealth(); + target = pUnit; + } + } + } + + return target; + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(!m_creature->HasAura(SPELL_BERSERK, 0)) + if(BerserkTimer < diff) + DoCast(m_creature, SPELL_BERSERK); + else BerserkTimer -= diff; + + if(SummonFlameTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 1); + + if(!target) // someone is trying to solo, set target as current victim. + target = m_creature->getVictim(); + + if(target) + { + Creature* MoltenFlame = SummonCreature(CREATURE_STALKER, target); + if(MoltenFlame) + { + // Invisible model + MoltenFlame->SetUInt32Value(UNIT_FIELD_DISPLAYID, 11686); + ((molten_flameAI*)MoltenFlame->AI())->SetSupremusGUID(m_creature->GetGUID()); + ((molten_flameAI*)MoltenFlame->AI())->StalkTarget(target); + SummonFlameTimer = 20000; + } + } + }else SummonFlameTimer -= diff; + + if(Phase1) + { + if(HurtfulStrikeTimer < diff) + { + Unit* target = CalculateHurtfulStrikeTarget(); + if(target) + { + DoCast(target, SPELL_HURTFUL_STRIKE); + HurtfulStrikeTimer = 5000; + } + }else HurtfulStrikeTimer -= diff; + } + + if(!Phase1) + { + if(SwitchTargetTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + DoResetThreat(); + m_creature->AddThreat(target, 5000000.0f); + DoTextEmote("acquires a new target!", NULL); + SwitchTargetTimer = 10000; + } + + }else SwitchTargetTimer -= diff; + + if(SummonVolcanoTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + + if(!target) + target = m_creature->getVictim(); + + if(target) + { + Creature* Volcano = NULL; + Volcano = SummonCreature(CREATURE_VOLCANO, target); + + if(Volcano) + { + DoCast(target, SPELL_VOLCANIC_ERUPTION); + ((npc_volcanoAI*)Volcano->AI())->SetSupremusGUID(m_creature->GetGUID()); + } + + DoTextEmote("roars and the ground begins to crack open!", NULL); + SummonVolcanoTimer = 10000; + } + }else SummonVolcanoTimer -= diff; + } + + if(PhaseSwitchTimer < diff) + { + if(!Phase1) + { + Phase1 = true; + DoResetThreat(); + PhaseSwitchTimer = 60000; + m_creature->SetSpeed(MOVE_RUN, 1.0f); + } + else + { + Phase1 = false; + DoResetThreat(); + SwitchTargetTimer = 10000; + SummonVolcanoTimer = 2000; + PhaseSwitchTimer = 60000; + m_creature->SetSpeed(MOVE_RUN, 0.9f); + } + }else PhaseSwitchTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_supremus(Creature *_Creature) +{ + return new boss_supremusAI (_Creature); +} + +CreatureAI* GetAI_molten_flame(Creature *_Creature) +{ + return new molten_flameAI (_Creature); +} + +CreatureAI* GetAI_npc_volcano(Creature *_Creature) +{ + return new npc_volcanoAI (_Creature); +} + +void AddSC_boss_supremus() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_supremus"; + newscript->GetAI = GetAI_boss_supremus; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="molten_flame"; + newscript->GetAI = GetAI_molten_flame; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_volcano"; + newscript->GetAI = GetAI_npc_volcano; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/boss_teron_gorefiend.cpp b/src/bindings/scripts/scripts/zone/black_temple/boss_teron_gorefiend.cpp new file mode 100644 index 00000000000..9c1182cfd0e --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/boss_teron_gorefiend.cpp @@ -0,0 +1,589 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Teron_Gorefiend +SD%Complete: 60 +SDComment: Requires Mind Control support for Ghosts. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "def_black_temple.h" + +//Spells +#define SPELL_INCINERATE 40239 +#define SPELL_CRUSHING_SHADOWS 40243 +#define SPELL_SHADOWBOLT 40185 +#define SPELL_PASSIVE_SHADOWFORM 40326 +#define SPELL_SHADOW_OF_DEATH 40251 +#define SPELL_BERSERK 45078 + +#define SPELL_ATROPHY 40327 // Shadowy Constructs use this when they get within melee range of a player + +//Speech'n'sound +#define SAY_INTRO "I was the first, you know. For me, the wheel of death has spun many times. So much time has passed. I have a lot of catching up to do..." +#define SOUND_INTRO 11512 + +#define SAY_AGGRO "Vengeance is mine!" +#define SOUND_AGGRO 11513 + +#define SAY_SLAY1 "I have use for you!" +#define SOUND_SLAY1 11514 + +#define SAY_SLAY2 "It gets worse..." +#define SOUND_SLAY2 11515 + +#define SAY_SPELL1 "What are you afraid of?" +#define SOUND_SPELL1 11517 + +#define SAY_SPELL2 "Death... really isn't so bad." +#define SOUND_SPELL2 11516 + +#define SAY_SPECIAL1 "Give in!" +#define SOUND_SPECIAL1 11518 + +#define SAY_SPECIAL2 "I have something for you..." +#define SOUND_SPECIAL2 11519 + +#define SAY_ENRAGE "YOU WILL SHOW THE PROPER RESPECT!" +#define SOUND_ENRAGE 11520 + +#define SAY_DEATH "The wheel...spins...again...." +#define SOUND_DEATH 11521 + +#define CREATURE_DOOM_BLOSSOM 23123 +#define CREATURE_SHADOWY_CONSTRUCT 23111 + +struct MANGOS_DLL_DECL mob_doom_blossomAI : public ScriptedAI +{ + mob_doom_blossomAI(Creature *c) : ScriptedAI(c) + { + Reset(); + } + + uint32 CheckTeronTimer; + uint32 ShadowBoltTimer; + uint64 TeronGUID; + + void Reset() + { + CheckTeronTimer = 5000; + ShadowBoltTimer = 12000; + TeronGUID = 0; + } + + void Aggro(Unit *who) { } + void AttackStart(Unit* who) { } + void MoveInLineOfSight(Unit* who) { } + + void UpdateAI(const uint32 diff) + { + if(CheckTeronTimer < diff) + { + if(TeronGUID) + { + DoZoneInCombat(); + + Creature* Teron = ((Creature*)Unit::GetUnit((*m_creature), TeronGUID)); + if((Teron) && (!Teron->isAlive() || Teron->IsInEvadeMode())) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + else + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + CheckTeronTimer = 5000; + }else CheckTeronTimer -= diff; + + if(!m_creature->getVictim() || !m_creature->SelectHostilTarget()) + return; + + if(ShadowBoltTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_SHADOWBOLT); + ShadowBoltTimer = 10000; + }else ShadowBoltTimer -= diff; + } + + void SetTeronGUID(uint64 guid){ TeronGUID = guid; } +}; + +//This is used to sort the players by distance for Constructs to see who to cast Atrophy on +struct TargetDistanceOrder : public std::binary_function +{ + 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)); + } +}; + +struct MANGOS_DLL_DECL mob_shadowy_constructAI : public ScriptedAI +{ + mob_shadowy_constructAI(Creature* c) : ScriptedAI(c) + { + Reset(); + } + + uint64 GhostGUID; + uint64 TeronGUID; + + uint32 CheckPlayerTimer; + uint32 CheckTeronTimer; + + void Reset() + { + GhostGUID = 0; + TeronGUID = 0; + + CheckPlayerTimer = 2000; + CheckTeronTimer = 5000; + } + + void Aggro(Unit* who) { } + + void MoveInLineOfSight(Unit *who) + { + if(!who || (!who->isAlive()) || (who->GetGUID() == GhostGUID)) + return; + + if(who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + m_creature->AddThreat(who, 1.0f); + } + } + } + +/* Comment it out for now. NOTE TO FUTURE DEV: UNCOMMENT THIS OUT ONLY AFTER MIND CONTROL IS IMPLEMENTED + void DamageTaken(Unit* done_by, uint32 &damage) + { + if(done_by->GetGUID() != GhostGUID) + damage = 0; // Only the ghost can deal damage. + } + */ + + void CheckPlayers() + { + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + if(m_threatlist.empty()) + return; // No threat list. Don't continue. + std::list::iterator itr = m_threatlist.begin(); + std::list targets; + for( ; itr != m_threatlist.end(); ++itr) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid()); + if(pUnit && pUnit->isAlive()) + targets.push_back(pUnit); + } + targets.sort(TargetDistanceOrder(m_creature)); + Unit* target = targets.front(); + if(target && m_creature->IsWithinDistInMap(target, m_creature->GetAttackDistance(target))) + { + DoCast(target, SPELL_ATROPHY); + m_creature->AI()->AttackStart(target); + } + } + + void UpdateAI(const uint32 diff) + { + if(CheckPlayerTimer < diff) + { + CheckPlayers(); + CheckPlayerTimer = 3000; + }else CheckPlayerTimer -= diff; + + if(CheckTeronTimer < diff) + { + Creature* Teron = ((Creature*)Unit::GetUnit((*m_creature), TeronGUID)); + if(!Teron || !Teron->isAlive() || Teron->IsInEvadeMode()) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + CheckTeronTimer = 5000; + }else CheckTeronTimer -= diff; + } +}; + +struct MANGOS_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI +{ + boss_teron_gorefiendAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 IncinerateTimer; + uint32 SummonDoomBlossomTimer; + uint32 EnrageTimer; + uint32 CrushingShadowsTimer; + uint32 ShadowOfDeathTimer; + uint32 SummonShadowsTimer; + uint32 RandomYellTimer; + uint32 AggroTimer; + + uint64 AggroTargetGUID; + uint64 GhostGUID; // Player that gets killed by Shadow of Death and gets turned into a ghost + + bool Intro; + + void Reset() + { + if(pInstance) + pInstance->SetData(DATA_TERONGOREFIENDEVENT, NOT_STARTED); + + IncinerateTimer = 20000 + rand()%11000; + SummonDoomBlossomTimer = 12000; + EnrageTimer = 600000; + CrushingShadowsTimer = 22000; + SummonShadowsTimer = 60000; + RandomYellTimer = 50000; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + // Start off unattackable so that the intro is done properly + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + AggroTimer = 20000; + AggroTargetGUID = 0; + Intro = false; + } + + void Aggro(Unit *who) {} + + void MoveInLineOfSight(Unit *who) + { + if(!who || (!who->isAlive())) return; + + if(who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + m_creature->AddThreat(who, 1.0f); + } + + if(!InCombat && !Intro && m_creature->IsWithinDistInMap(who, 200.0f) && (who->GetTypeId() == TYPEID_PLAYER)) + { + if(pInstance) + pInstance->SetData(DATA_TERONGOREFIENDEVENT, IN_PROGRESS); + + m_creature->GetMotionMaster()->Clear(false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DoYell(SAY_INTRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_INTRO); + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_TALK); + AggroTargetGUID = who->GetGUID(); + Intro = true; + } + } + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *victim) + { + if(pInstance) + pInstance->SetData(DATA_TERONGOREFIENDEVENT, DONE); + + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + float CalculateRandomLocation(float Loc, uint32 radius) + { + float coord = Loc; + switch(rand()%2) + { + case 0: + coord += rand()%radius; + break; + case 1: + coord -= rand()%radius; + break; + } + return coord; + } + + void SetThreatList(Creature* Blossom) + { + if(!Blossom) return; + + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + std::list::iterator i = m_threatlist.begin(); + for(i = m_threatlist.begin(); i != m_threatlist.end(); i++) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit && pUnit->isAlive()) + { + float threat = m_creature->getThreatManager().getThreat(pUnit); + Blossom->AddThreat(pUnit, threat); + } + } + } + + void MindControlGhost() + { + /************************************************************************/ + /** NOTE FOR FUTURE DEVELOPER: PROPERLY IMPLEMENT THE GHOST PORTION *****/ + /** ONLY AFTER MaNGOS FULLY IMPLEMENTS MIND CONTROL ABILITIES *****/ + /** THE CURRENT CODE IN THIS FUNCTION IS ONLY THE BEGINNING OF *****/ + /** WHAT IS FULLY NECESSARY FOR GOREFIEND TO BE 100% COMPLETE *****/ + /************************************************************************/ + + Unit* Ghost = NULL; + if(GhostGUID) + Ghost = Unit::GetUnit((*m_creature), GhostGUID); + if(Ghost && Ghost->isAlive() && Ghost->HasAura(SPELL_SHADOW_OF_DEATH, 0)) + { + /*float x,y,z; + Ghost->GetPosition(x,y,z); + Creature* control = m_creature->SummonCreature(CREATURE_GHOST, x, y, z, 0, TEMPSUMMON_TIMED_DESAWN, 30000); + if(control) + { + ((Player*)Ghost)->Possess(control); + Ghost->DealDamage(Ghost, Ghost->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, + false); + }*/ + for(uint8 i = 0; i < 4; ++i) + { + Creature* Construct = NULL; + float X = CalculateRandomLocation(Ghost->GetPositionX(), 10); + float Y = CalculateRandomLocation(Ghost->GetPositionY(), 10); + Construct = m_creature->SummonCreature(CREATURE_SHADOWY_CONSTRUCT, X, Y, Ghost->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000); + if(Construct) + { + Construct->CastSpell(Construct, SPELL_PASSIVE_SHADOWFORM, true); + SetThreatList(Construct); // Use same function as Doom Blossom to set Threat List. + ((mob_shadowy_constructAI*)Construct->AI())->GhostGUID = GhostGUID; + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(!target) // someone's trying to solo. + target = m_creature->getVictim(); + + if(target) + Construct->GetMotionMaster()->MoveChase(target); + } + } + } + } + + void UpdateAI(const uint32 diff) + { + if(Intro) + { + if(AggroTimer < diff) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); + Intro = false; + if(AggroTargetGUID) + { + Unit* pUnit = Unit::GetUnit((*m_creature), AggroTargetGUID); + if(pUnit) + { + m_creature->GetMotionMaster()->MoveChase(pUnit); + AttackStart(pUnit); + } + DoZoneInCombat(); + }else EnterEvadeMode(); + + }else AggroTimer -= diff; + } + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() || Intro) + return; + + if(SummonShadowsTimer < diff) + { + //MindControlGhost(); + + for(uint8 i = 0; i < 2; ++i) + { + Creature* Shadow = NULL; + float X = CalculateRandomLocation(m_creature->GetPositionX(), 10); + Shadow = m_creature->SummonCreature(CREATURE_SHADOWY_CONSTRUCT, X, m_creature->GetPositionY(), m_creature->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 0); + if(Shadow) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(!target) + target = m_creature->getVictim(); + + if(target) + Shadow->AI()->AttackStart(target); + } + } + SummonShadowsTimer = 60000; + }else SummonShadowsTimer -= diff; + + if(SummonDoomBlossomTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + float X = CalculateRandomLocation(target->GetPositionX(), 20); + float Y = CalculateRandomLocation(target->GetPositionY(), 20); + Creature* DoomBlossom = m_creature->SummonCreature(CREATURE_DOOM_BLOSSOM, X, Y, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 20000); + if(DoomBlossom) + { + DoomBlossom->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DoomBlossom->setFaction(m_creature->getFaction()); + DoomBlossom->AddThreat(target, 1.0f); + ((mob_doom_blossomAI*)DoomBlossom->AI())->SetTeronGUID(m_creature->GetGUID()); + ((mob_doom_blossomAI*)DoomBlossom->AI())->InCombat = true; + SetThreatList(DoomBlossom); + SummonDoomBlossomTimer = 35000; + } + } + }else SummonDoomBlossomTimer -= diff; + + if(IncinerateTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(!target) + target = m_creature->getVictim(); + + if(target) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SPECIAL1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SPECIAL1); + break; + case 1: + DoYell(SAY_SPECIAL2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SPECIAL2); + break; + } + DoCast(target, SPELL_INCINERATE); + IncinerateTimer = 20000 + rand()%31 * 1000; + } + }else IncinerateTimer -= diff; + + if(CrushingShadowsTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target && target->isAlive()) + DoCast(target, SPELL_CRUSHING_SHADOWS); + CrushingShadowsTimer = 10000 + rand()%16 * 1000; + }else CrushingShadowsTimer -= diff; + + /*** NOTE FOR FUTURE DEV: UNCOMMENT BELOW ONLY IF MIND CONTROL IS FULLY IMPLEMENTED **/ + /*if(ShadowOfDeathTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + + if(!target) + target = m_creature->getVictim(); + + if(target && target->isAlive() && target->GetTypeId() == TYPEID_PLAYER) + { + DoCast(target, SPELL_SHADOW_OF_DEATH); + GhostGUID = target->GetGUID(); + ShadowOfDeathTimer = 30000; + SummonShadowsTimer = 53000; // Make it VERY close but slightly less so that we can check if the aura is still on the player + } + }else ShadowOfDeathTimer -= diff;*/ + + if(RandomYellTimer < diff) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SPELL1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SPELL1); + break; + case 1: + DoYell(SAY_SPELL2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SPELL2); + break; + } + RandomYellTimer = 50000 + rand()%51 * 1000; + }else RandomYellTimer -= diff; + + if(!m_creature->HasAura(SPELL_BERSERK, 0)) + if(EnrageTimer < diff) + { + DoCast(m_creature, SPELL_BERSERK); + DoYell(SAY_ENRAGE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + }else EnrageTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_doom_blossom(Creature *_Creature) +{ + return new mob_doom_blossomAI(_Creature); +} + +CreatureAI* GetAI_mob_shadowy_construct(Creature *_Creature) +{ + return new mob_shadowy_constructAI(_Creature); +} + +CreatureAI* GetAI_boss_teron_gorefiend(Creature *_Creature) +{ + return new boss_teron_gorefiendAI (_Creature); +} + +void AddSC_boss_teron_gorefiend() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "mob_doom_blossom"; + newscript->GetAI = GetAI_mob_doom_blossom; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_shadowy_construct"; + newscript->GetAI = GetAI_mob_shadowy_construct; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_teron_gorefiend"; + newscript->GetAI = GetAI_boss_teron_gorefiend; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/boss_warlord_najentus.cpp b/src/bindings/scripts/scripts/zone/black_temple/boss_warlord_najentus.cpp new file mode 100644 index 00000000000..93d27c3d0d1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/boss_warlord_najentus.cpp @@ -0,0 +1,435 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Warlord_Najentus +SD%Complete: 90 +SDComment: Using a creature workaround instead of a GO for Impaling Spine. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "def_black_temple.h" + +//Aggro +#define SAY_AGGRO "You will die, in the name of Lady Vashj!" +#define SOUND_AGGRO 11450 + +//Needle (Random) +#define SAY_NEEDLE1 "Stick around!" +#define SOUND_NEEDLE1 11451 + +#define SAY_NEEDLE2 "I'll deal with you later!" +#define SOUND_NEEDLE2 11452 + +//Slay +#define SAY_SLAY1 "Your success was short lived!" +#define SOUND_SLAY1 11455 + +#define SAY_SLAY2 "Time for you to go!" +#define SOUND_SLAY2 11456 + +//Special +#define SAY_SPECIAL1 "Bel'anen dal'lorei!" +#define SOUND_SPECIAL1 11453 + +#define SAY_SPECIAL2 "Blood will flow!" +#define SOUND_SPECIAL2 11454 + +//Enrage +#define SAY_ENRAGE "My patience has ran out! Die, DIE!" +#define SOUND_ENRAGE 11458 + +//Death +#define SAY_DEATH "Lord Illidan will... crush you." +#define SOUND_DEATH 11459 + +//Spells +#define SPELL_CRASHINGWAVE 40100 +#define SPELL_NEEDLE_SPINE 39835 +#define SPELL_NEEDLE_AOE 39968 +#define SPELL_TIDAL_BURST 39878 +#define SPELL_TIDAL_SHIELD 39872 // Not going to use this since Hurl Spine doesn't dispel it. +#define SPELL_IMPALING_SPINE 39837 +#define SPELL_CREATE_NAJENTUS_SPINE 39956 +#define SPELL_HURL_SPINE 39948 +#define SPELL_SHIELD_VISUAL 37136 +#define SPELL_BERSERK 45078 + +#define DISPLAYID_SPINE 7362 + +struct MANGOS_DLL_DECL mob_najentus_spineAI : public ScriptedAI +{ + mob_najentus_spineAI(Creature *c) : ScriptedAI(c) + { + Reset(); + } + + uint64 SpineVictimGUID; + + void Reset() { SpineVictimGUID = 0; } + + void SetSpineVictimGUID(uint64 guid){ SpineVictimGUID = guid; } + + void JustDied(Unit *killer) + { + // Make the killer have the Najentus Spine item to pierce Tidal Shield + if(killer) + killer->CastSpell(killer, SPELL_CREATE_NAJENTUS_SPINE, true); + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if(damage < m_creature->GetHealth()) return; + + // Remove the Impaling Spine DoT from whoever was affected + if(SpineVictimGUID) + { + Unit* victim = Unit::GetUnit((*m_creature), SpineVictimGUID); + if(victim && ((victim->HasAura(SPELL_IMPALING_SPINE, 0)) || (victim->HasAura(SPELL_IMPALING_SPINE, 1)) || (victim->HasAura(SPELL_IMPALING_SPINE, 2)))) + victim->RemoveAurasDueToSpell(SPELL_IMPALING_SPINE); + } + } + + void Aggro(Unit* who) {} + void AttackStart(Unit* who) {} + void MoveInLineOfSight(Unit* who) {} + void UpdateAI(const uint32 diff) {} +}; + +struct MANGOS_DLL_DECL boss_najentusAI : public ScriptedAI +{ + boss_najentusAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + uint32 CrashingWaveTimer; + uint32 NeedleSpineTimer; + uint32 EnrageTimer; + uint32 SpecialYellTimer; + uint32 TidalShieldTimer; + uint32 ImpalingSpineTimer; + uint32 CheckTimer; // This timer checks if Najentus is Tidal Shielded and if so, regens health. If not, sets IsShielded to false + uint32 DispelShieldTimer; // This shield is only supposed to last 30 seconds, but the SPELL_SHIELD_VISUAL lasts forever + + uint64 SpineTargetGUID; + uint64 SpineGUID; + + bool IsShielded; + + void Reset() + { + IsShielded = false; + + CrashingWaveTimer = 28000; + NeedleSpineTimer = 10000; + EnrageTimer = 480000; + SpecialYellTimer = 45000 + (rand()%76)*1000; + TidalShieldTimer = 60000; + ImpalingSpineTimer = 45000; + CheckTimer = 2000; + DispelShieldTimer = 30000; + + SpineTargetGUID = 0; + SpineGUID = 0; + + if(pInstance) + { + if(m_creature->isAlive()) + { + pInstance->SetData(DATA_HIGHWARLORDNAJENTUSEVENT, NOT_STARTED); + ToggleGate(true); + } + else ToggleGate(false); + } + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *victim) + { + if(pInstance) + { + pInstance->SetData(DATA_HIGHWARLORDNAJENTUSEVENT, DONE); + ToggleGate(false); + } + + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + void ToggleGate(bool close) + { + if(GameObject* Gate = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_GAMEOBJECT_NAJENTUS_GATE))) + if(close) Gate->SetGoState(0); // Closed + else Gate->SetGoState(2); // Opened + } + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + if(IsShielded) + { + if(spell->Id == SPELL_HURL_SPINE) + { + if(m_creature->HasAura(SPELL_SHIELD_VISUAL, 0)) + m_creature->RemoveAurasDueToSpell(SPELL_SHIELD_VISUAL); + if(m_creature->HasAura(SPELL_TIDAL_SHIELD, 0)) + m_creature->RemoveAurasDueToSpell(SPELL_TIDAL_SHIELD); + DoCast(m_creature->getVictim(), SPELL_TIDAL_BURST); + IsShielded = false; + } + } + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if(IsShielded) + damage = 0; + } + + void Aggro(Unit *who) + { + if(pInstance) + pInstance->SetData(DATA_HIGHWARLORDNAJENTUSEVENT, IN_PROGRESS); + + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + + DoZoneInCombat(); + } + + // This is a workaround since we cannot summon GameObjects at will. + // Instead, we create a custom creature on top of the player. + // When someone kills the creature, the killer gets the "Naj'entus Spine" item. + // This item is the only item that should be able to pierce Tidal Shield + void DoImpalingSpineWorkaround(Unit* target) + { + Creature* Spine = NULL; + Spine = m_creature->SummonCreature(500000, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 10000); + if(Spine) + { + Spine->setFaction(m_creature->getFaction()); + ((mob_najentus_spineAI*)Spine->AI())->SetSpineVictimGUID(target->GetGUID()); + SpineTargetGUID = target->GetGUID(); + } + } + + bool RemoveImpalingSpine(uint64 guid) + { + if(!IsShielded || guid == SpineTargetGUID) return false; + + if(SpineTargetGUID) + { + Unit* target = Unit::GetUnit((*m_creature), SpineTargetGUID); + if(target && target->HasAura(SPELL_IMPALING_SPINE, 0)) + { + target->RemoveAurasDueToSpell(SPELL_IMPALING_SPINE); + return true; + } + } + + return false; + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(CheckTimer < diff) + { + // if(m_creature->HasAura(SPELL_TIDAL_SHIELD, 0)) + if(m_creature->HasAura(SPELL_SHIELD_VISUAL, 0)) + m_creature->SetHealth(m_creature->GetHealth() + (m_creature->GetMaxHealth()/100)); + else + IsShielded = false; + + CheckTimer = 2000; + }else CheckTimer -= diff; + + if(IsShielded) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + if(!m_creature->HasAura(SPELL_SHIELD_VISUAL, 0)) + DoCast(m_creature, SPELL_SHIELD_VISUAL); + if(DispelShieldTimer < diff) + { + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + if(m_creature->HasAura(SPELL_SHIELD_VISUAL, 0)) + m_creature->RemoveAurasDueToSpell(SPELL_SHIELD_VISUAL); + IsShielded = false; + }else DispelShieldTimer -= diff; + + return; // Don't cast or do anything while Shielded + } + + // Crashing Wave + if(CrashingWaveTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CRASHINGWAVE); + CrashingWaveTimer = 28500; + }else CrashingWaveTimer -= diff; + + if(!m_creature->HasAura(SPELL_BERSERK, 0)) + if(EnrageTimer < diff) + { + DoYell(SAY_ENRAGE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + DoCast(m_creature, SPELL_BERSERK); + }else EnrageTimer -= diff; + + // Needle + if(NeedleSpineTimer < diff) + { + for(uint8 i = 0; i < 3; ++i) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(!target) + target = m_creature->getVictim(); + + DoCast(target, SPELL_NEEDLE_AOE, true); + DoCast(target, SPELL_NEEDLE_SPINE); + } + + NeedleSpineTimer = 60000; + }else NeedleSpineTimer -= diff; + + if(SpecialYellTimer < diff) + { + switch(rand()%2) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_SPECIAL1); + break; + case 1: + DoYell(SAY_SPECIAL2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SPECIAL2); + break; + } + SpecialYellTimer = 25000 + (rand()%76)*1000; + }else SpecialYellTimer -= diff; + + if(ImpalingSpineTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(!target) + target = m_creature->getVictim(); + + if(target && (target->GetTypeId() == TYPEID_PLAYER)) + { + DoCast(target, SPELL_IMPALING_SPINE); + //DoImpalingSpineWorkaround(target); + SpineTargetGUID = target->GetGUID(); + ImpalingSpineTimer = 45000; + + switch(rand()%2) + { + case 0: + DoYell(SAY_NEEDLE1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_NEEDLE1); + break; + case 1: + DoYell(SAY_NEEDLE2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_NEEDLE2); + break; + } + } + }else ImpalingSpineTimer -= diff; + + if(TidalShieldTimer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature, SPELL_SHIELD_VISUAL, true); + // DoCast(m_creature, SPELL_TIDAL_SHIELD); + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + IsShielded = true; + TidalShieldTimer = 60000; + CheckTimer = 2000; + DispelShieldTimer = 30000; + }else TidalShieldTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +bool GOHello_go_najentus_spine(Player *player, GameObject* _GO) +{ + ScriptedInstance* pInstance = ((ScriptedInstance*)_GO->GetInstanceData()); + if(pInstance) + { + uint64 NajentusGUID = pInstance->GetData64(DATA_HIGHWARLORDNAJENTUS); + if(NajentusGUID) + { + Creature* Najentus = ((Creature*)Unit::GetUnit((*_GO), NajentusGUID)); + if(Najentus) + { + if(((boss_najentusAI*)Najentus->AI())->RemoveImpalingSpine(player->GetGUID())) + return true; + }else error_log("ERROR: Na'entus Spine GameObject unable to find Naj'entus"); + }else error_log("ERROR: Invalid GUID acquired for Naj'entus by Naj'entus Spine GameObject"); + } + else error_log("ERROR: Naj'entus Spine spawned in invalid instance or location"); + + return true; +} + +CreatureAI* GetAI_mob_najentus_spine(Creature *_Creature) +{ + return new mob_najentus_spineAI (_Creature); +} + +CreatureAI* GetAI_boss_najentus(Creature *_Creature) +{ + return new boss_najentusAI (_Creature); +} + +void AddSC_boss_najentus() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_najentus"; + newscript->GetAI = GetAI_boss_najentus; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_najentus_spine"; + newscript->GetAI = GetAI_mob_najentus_spine; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "go_najentus_spine"; + newscript->pGOHello = &GOHello_go_najentus_spine; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/def_black_temple.h b/src/bindings/scripts/scripts/zone/black_temple/def_black_temple.h new file mode 100644 index 00000000000..53e099f7835 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/def_black_temple.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_BLACK_TEMPLE_H +#define DEF_BLACK_TEMPLE_H + +#define DATA_AKAMA 1 +#define DATA_AKAMA_SHADE 2 +#define DATA_GURTOGGBLOODBOILEVENT 3 +#define DATA_HIGHWARLORDNAJENTUS 4 +#define DATA_HIGHWARLORDNAJENTUSEVENT 5 +#define DATA_ILLIDANSTORMRAGE 6 +#define DATA_ILLIDANSTORMRAGEEVENT 7 +#define DATA_ILLIDARICOUNCILEVENT 8 +#define DATA_ILLIDARICOUNCIL 9 +#define DATA_LADYMALANDE 10 +#define DATA_HIGHNETHERMANCERZEREVOR 11 +#define DATA_GATHIOSTHESHATTERER 12 +#define DATA_VERASDARKSHADOW 13 +#define DATA_MOTHERSHAHRAZEVENT 14 +#define DATA_RELIQUARYOFSOULSEVENT 15 +#define DATA_SHADEOFAKAMA 16 +#define DATA_SHADEOFAKAMAEVENT 17 +#define DATA_SUPREMUS 18 +#define DATA_SUPREMUSEVENT 19 +#define DATA_TERONGOREFIENDEVENT 20 +#define DATA_GAMEOBJECT_NAJENTUS_GATE 21 +#define DATA_GAMEOBJECT_ILLIDAN_GATE 22 +#define DATA_GAMEOBJECT_ILLIDAN_DOOR_R 23 +#define DATA_GAMEOBJECT_ILLIDAN_DOOR_L 24 +#define DATA_GAMEOBJECT_SUPREMUS_DOORS 25 +#define DATA_BLOOD_ELF_COUNCIL_VOICE 26 +#endif diff --git a/src/bindings/scripts/scripts/zone/black_temple/illidari_council.cpp b/src/bindings/scripts/scripts/zone/black_temple/illidari_council.cpp new file mode 100644 index 00000000000..4169f1d764f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/illidari_council.cpp @@ -0,0 +1,885 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Illidari_Council +SD%Complete: 95 +SDComment: Circle of Healing not working properly. +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "def_black_temple.h" + +// High Nethermancer Zerevor's spells +#define SPELL_FLAMESTRIKE 41481 +#define SPELL_BLIZZARD 41482 +#define SPELL_ARCANE_BOLT 41483 +#define SPELL_ARCANE_EXPLOSION 41524 +#define SPELL_DAMPEN_MAGIC 41478 + +// Lady Malande's spells +#define SPELL_EMPOWERED_SMITE 41471 +#define SPELL_CIRCLE_OF_HEALING 41455 +#define SPELL_REFLECTIVE_SHIELD 41475 +#define SPELL_DIVINE_WRATH 41472 +#define SPELL_HEAL_VISUAL 24171 + +// Gathios the Shatterer's spells +#define SPELL_BLESS_PROTECTION 41450 +#define SPELL_BLESS_SPELLWARD 41451 +#define SPELL_CONSECRATION 41541 +#define SPELL_HAMMER_OF_JUSTICE 41468 +#define SPELL_SEAL_OF_COMMAND 41469 +#define SPELL_SEAL_OF_BLOOD 41459 +#define SPELL_CHROMATIC_AURA 41453 +#define SPELL_DEVOTION_AURA 41452 + +// Veras Darkshadow's spells +#define SPELL_DEADLY_POISON 41485 +#define SPELL_ENVENOM 41487 +#define SPELL_VANISH 41479 + +#define SPELL_BERSERK 45078 + +//Speech'n'Sounds +#define SAY_GATH_AGGRO "I have better things to do!" +#define SOUND_GATH_AGGRO 11422 +#define SAY_GATH_SLAY "Selama am'oronor!" +#define SOUND_GATH_SLAY 11423 +#define SAY_GATH_COMNT "Well done!" +#define SOUND_GATH_COMNT 11424 +#define SAY_GATH_DEATH "Lord Illidan... I..." +#define SOUND_GATH_DEATH 11425 +#define SAY_GATH_SPECIAL1 "Enjoy your final moments!" +#define SOUND_GATH_SPECIAL1 11426 +#define SAY_GATH_SPECIAL2 "You are mine!" +#define SOUND_GATH_SPECIAL2 11427 + +#define SAY_MALA_AGGRO "Flee, or die!" +#define SOUND_MALA_AGGRO 11482 +#define SAY_MALA_SLAY "My work is done." +#define SOUND_MALA_SLAY 11483 +#define SAY_MALA_COMNT "As it should be!" +#define SOUND_MALA_COMNT 11484 +#define SAY_MALA_DEATH "Destiny... awaits." +#define SOUND_MALA_DEATH 11485 +#define SAY_MALA_SPECIAL1 "No second chances!" +#define SOUND_MALA_SPECIAL1 11486 +#define SAY_MALA_SPECIAL2 "I'm full of surprises!" +#define SOUND_MALA_SPECIAL2 11487 + +#define SAY_ZERE_AGGRO "Common... such a crude language. Bandal!" +#define SOUND_ZERE_AGGRO 11440 +#define SAY_ZERE_SLAY "Shorel'aran." +#define SOUND_ZERE_SLAY 11441 +#define SAY_ZERE_COMNT "Belesa menoor!" +#define SOUND_ZERE_COMNT 11442 +#define SAY_ZERE_DEATH "Diel ma'ahn... oreindel'o" +#define SOUND_ZERE_DEATH 11443 +#define SAY_ZERE_SPECIAL1 "Diel fin'al" +#define SOUND_ZERE_SPECIAL1 11444 +#define SAY_ZERE_SPECIAL2 "Sha'amoor ara mashal?" +#define SOUND_ZERE_SPECIAL2 11445 + +#define SAY_VERA_AGGRO "You wish to test me?" +#define SOUND_VERA_AGGRO 11524 +#define SAY_VERA_SLAY "Valiant effort!" +#define SOUND_VERA_SLAY 11525 +#define SAY_VERA_COMNT "A glorious kill!" +#define SOUND_VERA_COMNT 11526 +#define SAY_VERA_DEATH "You got lucky!" +#define SOUND_VERA_DEATH 11527 +#define SAY_VERA_SPECIAL1 "You're not caught up for this!" +#define SOUND_VERA_SPECIAL1 11528 +#define SAY_VERA_SPECIAL2 "Anar'alah belore!" +#define SOUND_VERA_SPECIAL2 11529 + +#define ERROR_INST_DATA "SD2 ERROR: Instance Data for Black Temple not set properly; Illidari Council event will not function properly." + +struct CouncilYells +{ + char* text; + uint32 soundId, timer; +}; + +static CouncilYells CouncilAggro[]= +{ + {"I have better things to do!", 11422, 5000}, // Gathios + {"You wish to test me?", 11524, 5500}, // Veras + {"Flee, or die!", 11482, 5000}, // Malande + {"Common... such a crude language. Bandal!", 11440, 0}, // Zerevor +}; + +// Need to get proper timers for this later +static CouncilYells CouncilEnrage[]= +{ + {"Enough games!", 11428, 2000}, // Gathios + {"You wish to kill me? Hahaha, you first!", 11530, 6000},//Veras + {"For Quel'Thalas! For the Sunwell!", 11488, 5000}, // Malande + {"Sha'amoor sine menoor!", 11446, 0}, // Zerevor +}; + +struct MANGOS_DLL_DECL mob_blood_elf_council_voice_triggerAI : public ScriptedAI +{ + mob_blood_elf_council_voice_triggerAI(Creature* c) : ScriptedAI(c) + { + for(uint8 i = 0; i < 4; ++i) + Council[i] = 0; + Reset(); + } + + uint64 Council[4]; + + uint32 EnrageTimer; + uint32 AggroYellTimer; + + uint8 YellCounter; // Serves as the counter for both the aggro and enrage yells + + bool EventStarted; + + void Reset() + { + EnrageTimer = 900000; // 15 minutes + AggroYellTimer = 500; + + YellCounter = 0; + + EventStarted = false; + } + + // finds and stores the GUIDs for each Council member using instance data system. + void LoadCouncilGUIDs() + { + if(ScriptedInstance* pInstance = ((ScriptedInstance*)m_creature->GetInstanceData())) + { + Council[0] = pInstance->GetData64(DATA_GATHIOSTHESHATTERER); + Council[1] = pInstance->GetData64(DATA_VERASDARKSHADOW); + Council[2] = pInstance->GetData64(DATA_LADYMALANDE); + Council[3] = pInstance->GetData64(DATA_HIGHNETHERMANCERZEREVOR); + } + else error_log(ERROR_INST_DATA); + } + + void Aggro(Unit* who) {} + + void AttackStart(Unit* who) {} + void MoveInLineOfSight(Unit* who) {} + + void UpdateAI(const uint32 diff) + { + if(!EventStarted) + return; + + if(YellCounter > 3) + return; + + if(AggroYellTimer) + if(AggroYellTimer <= diff) + { + if(Unit* pMember = Unit::GetUnit(*m_creature, Council[YellCounter])) + { + pMember->MonsterYell(CouncilAggro[YellCounter].text, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(pMember, CouncilAggro[YellCounter].soundId); + AggroYellTimer = CouncilAggro[YellCounter].timer; + } + ++YellCounter; + if(YellCounter > 3) + YellCounter = 0; // Reuse for Enrage Yells + }else AggroYellTimer -= diff; + + if(EnrageTimer) + if(EnrageTimer <= diff) + { + if(Unit* pMember = Unit::GetUnit(*m_creature, Council[YellCounter])) + { + pMember->CastSpell(pMember, SPELL_BERSERK, true); + pMember->MonsterYell(CouncilEnrage[YellCounter].text, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(pMember, CouncilEnrage[YellCounter].soundId); + EnrageTimer = CouncilEnrage[YellCounter].timer; + } + ++YellCounter; + }else EnrageTimer -= diff; + } +}; + +struct MANGOS_DLL_DECL mob_illidari_councilAI : public ScriptedAI +{ + mob_illidari_councilAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + for(uint8 i = 0; i < 4; ++i) + Council[i] = 0; + + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 Council[4]; + + uint32 CheckTimer; + uint32 EndEventTimer; + + uint8 DeathCount; + + bool EventBegun; + + void Reset() + { + CheckTimer = 2000; + EndEventTimer = 0; + + DeathCount = 0; + + Creature* pMember = NULL; + for(uint8 i = 0; i < 4; ++i) + { + if(pMember = ((Creature*)Unit::GetUnit((*m_creature), Council[i]))) + { + if(!pMember->isAlive()) + { + pMember->RemoveCorpse(); + pMember->Respawn(); + } + pMember->AI()->EnterEvadeMode(); + } + } + + if(pInstance) + { + pInstance->SetData(DATA_ILLIDARICOUNCILEVENT, NOT_STARTED); + if(Creature* VoiceTrigger = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_BLOOD_ELF_COUNCIL_VOICE)))) + VoiceTrigger->AI()->EnterEvadeMode(); + } + + EventBegun = false; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, 11686); + } + + void Aggro(Unit *who) {} + void AttackStart(Unit* who) {} + void MoveInLineOfSight(Unit* who) {} + + void StartEvent(Unit *target) + { + if(!pInstance) return; + + if(target && target->isAlive()) + { + Council[0] = pInstance->GetData64(DATA_GATHIOSTHESHATTERER); + Council[1] = pInstance->GetData64(DATA_HIGHNETHERMANCERZEREVOR); + Council[2] = pInstance->GetData64(DATA_LADYMALANDE); + Council[3] = pInstance->GetData64(DATA_VERASDARKSHADOW); + + // Start the event for the Voice Trigger + if(Creature* VoiceTrigger = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_BLOOD_ELF_COUNCIL_VOICE)))) + { + ((mob_blood_elf_council_voice_triggerAI*)VoiceTrigger->AI())->LoadCouncilGUIDs(); + ((mob_blood_elf_council_voice_triggerAI*)VoiceTrigger->AI())->EventStarted = true; + } + + for(uint8 i = 0; i < 4; ++i) + { + Unit* Member = NULL; + if(Council[i]) + { + Member = Unit::GetUnit((*m_creature), Council[i]); + if(Member && Member->isAlive()) + Member->AddThreat(target, 1.0f); + } + } + + pInstance->SetData(DATA_ILLIDARICOUNCILEVENT, IN_PROGRESS); + + EventBegun = true; + } + } + + void UpdateAI(const uint32 diff) + { + if(!EventBegun) return; + + if(EndEventTimer) + if(EndEventTimer <= diff) + { + if(DeathCount > 3) + { + if(pInstance) + { + if(Creature* VoiceTrigger = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_BLOOD_ELF_COUNCIL_VOICE)))) + VoiceTrigger->DealDamage(VoiceTrigger, VoiceTrigger->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + pInstance->SetData(DATA_ILLIDARICOUNCILEVENT, DONE); + } + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + return; + } + + Creature* pMember = ((Creature*)Unit::GetUnit(*m_creature, Council[DeathCount])); + if(pMember && pMember->isAlive()) + pMember->DealDamage(pMember, pMember->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + ++DeathCount; + EndEventTimer = 1500; + }else EndEventTimer -= diff; + + if(CheckTimer) + if(CheckTimer <= diff) + { + uint8 EvadeCheck = 0; + for(uint8 i = 0; i < 4; ++i) + { + if(Council[i]) + { + if(Creature* Member = ((Creature*)Unit::GetUnit((*m_creature), Council[i]))) + { + // This is the evade/death check. + if(Member->isAlive() && !Member->SelectHostilTarget()) + ++EvadeCheck; //If all members evade, we reset so that players can properly reset the event + else if(!Member->isAlive()) // If even one member dies, kill the rest, set instance data, and kill self. + { + EndEventTimer = 1000; + CheckTimer = 0; + return; + } + } + } + } + + if(EvadeCheck > 3) + Reset(); + + CheckTimer = 2000; + }else CheckTimer -= diff; + + } +}; + +struct MANGOS_DLL_DECL boss_illidari_councilAI : public ScriptedAI +{ + boss_illidari_councilAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + for(uint8 i = 0; i < 4; ++i) + Council[i] = 0; + LoadedGUIDs = false; + } + + uint64 Council[4]; + + ScriptedInstance* pInstance; + + bool LoadedGUIDs; + + void Aggro(Unit* who) + { + if(pInstance) + { + Creature* Controller = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_ILLIDARICOUNCIL))); + if(Controller) + ((mob_illidari_councilAI*)Controller->AI())->StartEvent(who); + } + else + { + error_log(ERROR_INST_DATA); + EnterEvadeMode(); + } + DoZoneInCombat(); + // Load GUIDs on first aggro because the creature guids are only set as the creatures are created in world- + // this means that for each creature, it will attempt to LoadGUIDs even though some of the other creatures are + // not in world, and thus have no GUID set in the instance data system. Putting it in aggro ensures that all the creatures + // have been loaded and have their GUIDs set in the instance data system. + if(!LoadedGUIDs) + LoadGUIDs(); + } + + void DamageTaken(Unit* done_by, uint32 &damage) + { + if(done_by == m_creature) + return; + + damage /= 4; + for(uint8 i = 0; i < 4; ++i) + { + if(Unit* pUnit = Unit::GetUnit(*m_creature, Council[i])) + if(pUnit != m_creature && damage < pUnit->GetHealth()) + pUnit->SetHealth(pUnit->GetHealth() - damage); + } + } + + void LoadGUIDs() + { + if(!pInstance) + { + error_log(ERROR_INST_DATA); + return; + } + + Council[0] = pInstance->GetData64(DATA_LADYMALANDE); + Council[1] = pInstance->GetData64(DATA_HIGHNETHERMANCERZEREVOR); + Council[2] = pInstance->GetData64(DATA_GATHIOSTHESHATTERER); + Council[3] = pInstance->GetData64(DATA_VERASDARKSHADOW); + + LoadedGUIDs = true; + } +}; + +struct MANGOS_DLL_DECL boss_gathios_the_shattererAI : public boss_illidari_councilAI +{ + boss_gathios_the_shattererAI(Creature *c) : boss_illidari_councilAI(c) { Reset(); } + + uint32 ConsecrationTimer; + uint32 HammerOfJusticeTimer; + uint32 SealTimer; + uint32 AuraTimer; + uint32 BlessingTimer; + + void Reset() + { + ConsecrationTimer = 40000; + HammerOfJusticeTimer = 10000; + SealTimer = 40000; + AuraTimer = 90000; + BlessingTimer = 60000; + } + + void KilledUnit(Unit *victim) + { + DoYell(SAY_GATH_SLAY,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_GATH_SLAY); + } + + void JustDied(Unit *victim) + { + DoYell(SAY_GATH_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_GATH_DEATH); + } + + Unit* SelectCouncilMember() + { + Unit* pUnit = m_creature; + uint32 member = 0; // He chooses Lady Malande most often + + if(rand()%10 == 0) // But there is a chance he picks someone else. + member = urand(1, 3); + + if(member != 2) // No need to create another pointer to us using Unit::GetUnit + pUnit = Unit::GetUnit((*m_creature), Council[member]); + return pUnit; + } + + void CastAuraOnCouncil() + { + uint32 spellid = 0; + switch(rand()%2) + { + case 0: spellid = SPELL_DEVOTION_AURA; break; + case 1: spellid = SPELL_CHROMATIC_AURA; break; + } + for(uint8 i = 0; i < 4; ++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), Council[i]); + if(pUnit) + pUnit->CastSpell(pUnit, spellid, true, 0, 0, m_creature->GetGUID()); + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(BlessingTimer < diff) + { + if(Unit* pUnit = SelectCouncilMember()) + { + switch(rand()%2) + { + case 0: DoCast(pUnit, SPELL_BLESS_SPELLWARD); break; + case 1: DoCast(pUnit, SPELL_BLESS_PROTECTION); break; + } + } + BlessingTimer = 60000; + }else BlessingTimer -= diff; + + if(ConsecrationTimer < diff) + { + DoCast(m_creature, SPELL_CONSECRATION); + ConsecrationTimer = 40000; + }else ConsecrationTimer -= diff; + + if(HammerOfJusticeTimer < diff) + { + if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0)) + { + // is in ~10-40 yd range + if(m_creature->GetDistance2d(target) > 10 && m_creature->GetDistance2d(target) < 40) + { + DoCast(target, SPELL_HAMMER_OF_JUSTICE); + HammerOfJusticeTimer = 20000; + } + } + }else HammerOfJusticeTimer -= diff; + + if(SealTimer < diff) + { + switch(rand()%2) + { + case 0: DoCast(m_creature, SPELL_SEAL_OF_COMMAND); break; + case 1: DoCast(m_creature, SPELL_SEAL_OF_BLOOD); break; + } + SealTimer = 40000; + }else SealTimer -= diff; + + if(AuraTimer < diff) + { + CastAuraOnCouncil(); + AuraTimer = 90000; + }else AuraTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_high_nethermancer_zerevorAI : public boss_illidari_councilAI +{ + boss_high_nethermancer_zerevorAI(Creature *c) : boss_illidari_councilAI(c) { Reset(); } + + uint32 BlizzardTimer; + uint32 FlamestrikeTimer; + uint32 ArcaneBoltTimer; + uint32 DampenMagicTimer; + uint32 Cooldown; + uint32 ArcaneExplosionTimer; + + void Reset() + { + BlizzardTimer = 30000 + rand()%61 * 1000; + FlamestrikeTimer = 30000 + rand()%61 * 1000; + ArcaneBoltTimer = 10000; + DampenMagicTimer = 2000; + ArcaneExplosionTimer = 14000; + Cooldown = 0; + } + + void KilledUnit(Unit *victim) + { + DoYell(SAY_ZERE_SLAY,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_ZERE_SLAY); + } + + void JustDied(Unit *victim) + { + DoYell(SAY_ZERE_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_ZERE_DEATH); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(Cooldown) + { + if(Cooldown < diff) Cooldown = 0; + else + { + Cooldown -= diff; + return; // Don't cast any other spells if global cooldown is still ticking + } + } + + if(DampenMagicTimer < diff) + { + DoCast(m_creature, SPELL_DAMPEN_MAGIC); + Cooldown = 1000; + DampenMagicTimer = 110000; // almost 2 minutes + ArcaneBoltTimer += 1000; // Give the Mage some time to spellsteal Dampen. + }else DampenMagicTimer -= diff; + + if(ArcaneExplosionTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ARCANE_EXPLOSION); + Cooldown = 1000; + ArcaneExplosionTimer = 14000; + }else ArcaneExplosionTimer -= diff; + + if(ArcaneBoltTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ARCANE_BOLT); + ArcaneBoltTimer = 3000; + Cooldown = 2000; + }else ArcaneBoltTimer -= diff; + + if(BlizzardTimer < diff) + { + if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0)) + { + DoCast(target, SPELL_BLIZZARD); + BlizzardTimer = 45000 + rand()%46 * 1000; + FlamestrikeTimer += 10000; + Cooldown = 1000; + } + }else BlizzardTimer -= diff; + + if(FlamestrikeTimer < diff) + { + if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0)) + { + DoCast(target, SPELL_FLAMESTRIKE); + FlamestrikeTimer = 55000 + rand()%46 * 1000; + BlizzardTimer += 10000; + Cooldown = 2000; + } + }else FlamestrikeTimer -= diff; + } +}; + +struct MANGOS_DLL_DECL boss_lady_malandeAI : public boss_illidari_councilAI +{ + boss_lady_malandeAI(Creature *c) : boss_illidari_councilAI(c) { Reset(); } + + uint32 EmpoweredSmiteTimer; + uint32 CircleOfHealingTimer; + uint32 DivineWrathTimer; + uint32 ReflectiveShieldTimer; + + void Reset() + { + EmpoweredSmiteTimer = 38000; + CircleOfHealingTimer = 20000; + DivineWrathTimer = 40000; + ReflectiveShieldTimer = 0; + } + + void KilledUnit(Unit *victim) + { + DoYell(SAY_MALA_SLAY,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_MALA_SLAY); + } + + void JustDied(Unit *victim) + { + DoYell(SAY_MALA_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_MALA_DEATH); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(EmpoweredSmiteTimer < diff) + { + if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0)) + { + DoCast(target, SPELL_EMPOWERED_SMITE); + EmpoweredSmiteTimer = 38000; + } + }else EmpoweredSmiteTimer -= diff; + + if(CircleOfHealingTimer < diff) + { + //Currently bugged and puts Malande on the threatlist of the other council members. It also heals players. + //DoCast(m_creature, SPELL_CIRCLE_OF_HEALING); + CircleOfHealingTimer = 60000; + }else CircleOfHealingTimer -= diff; + + if(DivineWrathTimer < diff) + { + if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0)) + { + DoCast(target, SPELL_DIVINE_WRATH); + DivineWrathTimer = 40000 + rand()%41 * 1000; + } + }else DivineWrathTimer -= diff; + + if(ReflectiveShieldTimer < diff) + { + DoCast(m_creature, SPELL_REFLECTIVE_SHIELD); + ReflectiveShieldTimer = 65000; + }else ReflectiveShieldTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_veras_darkshadowAI : public boss_illidari_councilAI +{ + boss_veras_darkshadowAI(Creature *c) : boss_illidari_councilAI(c) { Reset(); } + + uint64 EnvenomTargetGUID; + + uint32 DeadlyPoisonTimer; + uint32 VanishTimer; + uint32 AppearEnvenomTimer; + + bool HasVanished; + + void Reset() + { + EnvenomTargetGUID = 0; + + DeadlyPoisonTimer = 20000; + VanishTimer = 60000 + rand()%61 * 1000; + AppearEnvenomTimer = 150000; + + HasVanished = false; + m_creature->SetVisibility(VISIBILITY_ON); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void KilledUnit(Unit *victim) + { + DoYell(SAY_VERA_SLAY,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_VERA_SLAY); + } + + void JustDied(Unit *victim) + { + DoYell(SAY_VERA_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_VERA_DEATH); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(!HasVanished) + { + if(DeadlyPoisonTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_DEADLY_POISON); + DeadlyPoisonTimer = 15000 + rand()%31 * 1000; + }else DeadlyPoisonTimer -= diff; + + if(AppearEnvenomTimer < diff) // Cast Envenom. This is cast 4 seconds after Vanish is over + { + DoCast(m_creature->getVictim(), SPELL_ENVENOM); + AppearEnvenomTimer = 90000; + }else AppearEnvenomTimer -= diff; + + if(VanishTimer < diff) // Disappear and stop attacking, but follow a random unit + { + if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0)) + { + VanishTimer = 30000; + AppearEnvenomTimer= 28000; + HasVanished = true; + m_creature->SetVisibility(VISIBILITY_OFF); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DoResetThreat(); + // Chase a unit. Check before DoMeleeAttackIfReady prevents from attacking + m_creature->AddThreat(target, 500000.0f); + m_creature->GetMotionMaster()->MoveChase(target); + } + }else VanishTimer -= diff; + + DoMeleeAttackIfReady(); + } + else + { + if(VanishTimer < diff) // Become attackable and poison current target + { + Unit* target = m_creature->getVictim(); + DoCast(target, SPELL_DEADLY_POISON); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DoResetThreat(); + m_creature->AddThreat(target, 3000.0f); // Make Veras attack his target for a while, he will cast Envenom 4 seconds after. + DeadlyPoisonTimer += 6000; + VanishTimer = 90000; + AppearEnvenomTimer = 4000; + HasVanished = false; + }else VanishTimer -= diff; + + if(AppearEnvenomTimer < diff) // Appear 2 seconds before becoming attackable (Shifting out of vanish) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + m_creature->SetVisibility(VISIBILITY_ON); + AppearEnvenomTimer = 6000; + }else AppearEnvenomTimer -= diff; + } + } +}; + +CreatureAI* GetAI_mob_blood_elf_council_voice_trigger(Creature* c) +{ + return new mob_blood_elf_council_voice_triggerAI(c); +} + +CreatureAI* GetAI_mob_illidari_council(Creature *_Creature) +{ + return new mob_illidari_councilAI (_Creature); +} + +CreatureAI* GetAI_boss_gathios_the_shatterer(Creature *_Creature) +{ + return new boss_gathios_the_shattererAI (_Creature); +} + +CreatureAI* GetAI_boss_lady_malande(Creature *_Creature) +{ + return new boss_lady_malandeAI (_Creature); +} + +CreatureAI* GetAI_boss_veras_darkshadow(Creature *_Creature) +{ + return new boss_veras_darkshadowAI (_Creature); +} + +CreatureAI* GetAI_boss_high_nethermancer_zerevor(Creature *_Creature) +{ + return new boss_high_nethermancer_zerevorAI (_Creature); +} + +void AddSC_boss_illidari_council() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mob_illidari_council"; + newscript->GetAI = GetAI_mob_illidari_council; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_blood_elf_council_voice_trigger"; + newscript->GetAI = GetAI_mob_blood_elf_council_voice_trigger; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_gathios_the_shatterer"; + newscript->GetAI = GetAI_boss_gathios_the_shatterer; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_lady_malande"; + newscript->GetAI = GetAI_boss_lady_malande; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_veras_darkshadow"; + newscript->GetAI = GetAI_boss_veras_darkshadow; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_high_nethermancer_zerevor"; + newscript->GetAI = GetAI_boss_high_nethermancer_zerevor; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/black_temple/instance_black_temple.cpp b/src/bindings/scripts/scripts/zone/black_temple/instance_black_temple.cpp new file mode 100644 index 00000000000..3251c93cafa --- /dev/null +++ b/src/bindings/scripts/scripts/zone/black_temple/instance_black_temple.cpp @@ -0,0 +1,252 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Black_Temple +SD%Complete: 100 +SDComment: Instance Data Scripts and functions to acquire mobs and set encounter status for use in various Black Temple Scripts +SDCategory: Black Temple +EndScriptData */ + +#include "precompiled.h" +#include "def_black_temple.h" + +#define ENCOUNTERS 9 + +/* Black Temple encounters: +0 - High Warlord Naj'entus event +1 - Supremus Event +2 - Shade of Akama Event +3 - Teron Gorefiend Event +4 - Gurtogg Bloodboil Event +5 - Reliquary Of Souls Event +6 - Mother Shahraz Event +7 - Illidari Council Event +8 - Illidan Stormrage Event +*/ + +struct MANGOS_DLL_DECL instance_black_temple : public ScriptedInstance +{ + instance_black_temple(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint64 Najentus; + uint64 Akama; // This is the Akama that starts the Illidan encounter. + uint64 Akama_Shade; // This is the Akama that starts the Shade of Akama encounter. + uint64 ShadeOfAkama; + uint64 Supremus; + uint64 LadyMalande; + uint64 GathiosTheShatterer; + uint64 HighNethermancerZerevor; + uint64 VerasDarkshadow; + uint64 IllidariCouncil; + uint64 BloodElfCouncilVoice; + uint64 IllidanStormrage; + + uint64 NajentusGate; + uint64 MainTempleDoors; + uint64 IllidanGate; + uint64 IllidanDoor[2]; + + uint32 Encounters[ENCOUNTERS]; + + void Initialize() + { + Najentus = 0; + Akama = 0; + Akama_Shade = 0; + ShadeOfAkama = 0; + Supremus = 0; + LadyMalande = 0; + GathiosTheShatterer = 0; + HighNethermancerZerevor = 0; + VerasDarkshadow = 0; + IllidariCouncil = 0; + BloodElfCouncilVoice = 0; + IllidanStormrage = 0; + + NajentusGate = 0; + MainTempleDoors = 0; + IllidanGate = 0; + IllidanDoor[0] = 0; + IllidanDoor[1] = 0; + + for(uint8 i = 0; i < ENCOUNTERS; i++) + Encounters[i] = NOT_STARTED; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < ENCOUNTERS; i++) + if(Encounters[i] == IN_PROGRESS) return true; + + return false; + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case 22887: Najentus = creature->GetGUID(); break; + case 23089: Akama = creature->GetGUID(); break; + case 22990: Akama_Shade = creature->GetGUID(); break; + case 22841: ShadeOfAkama = creature->GetGUID(); break; + case 22898: Supremus = creature->GetGUID(); break; + case 22917: IllidanStormrage = creature->GetGUID(); break; + case 22949: GathiosTheShatterer = creature->GetGUID(); break; + case 22950: HighNethermancerZerevor = creature->GetGUID(); break; + case 22951: LadyMalande = creature->GetGUID(); break; + case 22952: VerasDarkshadow = creature->GetGUID(); break; + case 23426: IllidariCouncil = creature->GetGUID(); break; + case 23499: BloodElfCouncilVoice = creature->GetGUID(); break; + } + } + + void OnObjectCreate(GameObject* go) + { + switch(go->GetEntry()) + { + case 185483: // Gate past Naj'entus (at the entrance to Supermoose's courtyards) + NajentusGate = go->GetGUID(); + break; + case 185882: // Main Temple Doors - right past Supermoose (Supremus) + MainTempleDoors = go->GetGUID(); + break; + case 185905: // Gate leading to Temple Summit + IllidanGate = go->GetGUID(); + break; + case 186261: // Right door at Temple Summit + IllidanDoor[0] = go->GetGUID(); + break; + case 186262: // Left door at Temple Summit + IllidanDoor[1] = go->GetGUID(); + break; + } + } + + uint64 GetData64(uint32 identifier) + { + switch(identifier) + { + case DATA_HIGHWARLORDNAJENTUS: return Najentus; + case DATA_AKAMA: return Akama; + case DATA_AKAMA_SHADE: return Akama_Shade; + case DATA_SHADEOFAKAMA: return ShadeOfAkama; + case DATA_SUPREMUS: return Supremus; + case DATA_ILLIDANSTORMRAGE: return IllidanStormrage; + case DATA_GATHIOSTHESHATTERER: return GathiosTheShatterer; + case DATA_HIGHNETHERMANCERZEREVOR: return HighNethermancerZerevor; + case DATA_LADYMALANDE: return LadyMalande; + case DATA_VERASDARKSHADOW: return VerasDarkshadow; + case DATA_ILLIDARICOUNCIL: return IllidariCouncil; + case DATA_GAMEOBJECT_NAJENTUS_GATE: return NajentusGate; + case DATA_GAMEOBJECT_ILLIDAN_GATE: return IllidanGate; + case DATA_GAMEOBJECT_ILLIDAN_DOOR_R: return IllidanDoor[0]; + case DATA_GAMEOBJECT_ILLIDAN_DOOR_L: return IllidanDoor[1]; + case DATA_GAMEOBJECT_SUPREMUS_DOORS: return MainTempleDoors; + case DATA_BLOOD_ELF_COUNCIL_VOICE: return BloodElfCouncilVoice; + } + + return 0; + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_HIGHWARLORDNAJENTUSEVENT: Encounters[0] = data; break; + case DATA_SUPREMUSEVENT: Encounters[1] = data; break; + case DATA_SHADEOFAKAMAEVENT: Encounters[2] = data; break; + case DATA_TERONGOREFIENDEVENT: Encounters[3] = data; break; + case DATA_GURTOGGBLOODBOILEVENT: Encounters[4] = data; break; + case DATA_RELIQUARYOFSOULSEVENT: Encounters[5] = data; break; + case DATA_MOTHERSHAHRAZEVENT: Encounters[6] = data; break; + case DATA_ILLIDARICOUNCILEVENT: Encounters[7] = data; break; + case DATA_ILLIDANSTORMRAGEEVENT: Encounters[8] = data; break; + } + + if(data == DONE) + SaveToDB(); + } + + uint32 GetData(uint32 type) + { + switch(type) + { + case DATA_HIGHWARLORDNAJENTUSEVENT: return Encounters[0]; + case DATA_SUPREMUSEVENT: return Encounters[1]; + case DATA_SHADEOFAKAMAEVENT: return Encounters[2]; + case DATA_TERONGOREFIENDEVENT: return Encounters[3]; + case DATA_GURTOGGBLOODBOILEVENT: return Encounters[4]; + case DATA_RELIQUARYOFSOULSEVENT: return Encounters[5]; + case DATA_MOTHERSHAHRAZEVENT: return Encounters[6]; + case DATA_ILLIDARICOUNCILEVENT: return Encounters[7]; + case DATA_ILLIDANSTORMRAGEEVENT: return Encounters[8]; + } + + return 0; + } + + const char* Save() + { + OUT_SAVE_INST_DATA; + std::ostringstream stream; + stream << Encounters[0] << " " << Encounters[1] << " " << Encounters[2] << " " + << Encounters[3] << " " << Encounters[4] << " " << Encounters[5] << " " + << Encounters[6] << " " << Encounters[7] << " " << Encounters[8]; + char* out = new char[stream.str().length() + 1]; + strcpy(out, stream.str().c_str()); + if(out) + { + OUT_SAVE_INST_DATA_COMPLETE; + return out; + } + + return NULL; + } + + void Load(const char* in) + { + if(!in) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(in); + std::istringstream stream(in); + stream >> Encounters[0] >> Encounters[1] >> Encounters[2] >> Encounters[3] + >> Encounters[4] >> Encounters[5] >> Encounters[6] >> Encounters[7] + >> Encounters[8]; + for(uint8 i = 0; i < ENCOUNTERS; ++i) + if(Encounters[i] == IN_PROGRESS) // Do not load an encounter as "In Progress" - reset it instead. + Encounters[i] = NOT_STARTED; + OUT_LOAD_INST_DATA_COMPLETE; + } +}; + +InstanceData* GetInstanceData_instance_black_temple(Map* map) +{ + return new instance_black_temple(map); +} + +void AddSC_instance_black_temple() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_black_temple"; + newscript->GetInstanceData = GetInstanceData_instance_black_temple; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/blackrock_depths.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/blackrock_depths.cpp new file mode 100644 index 00000000000..b4b2b177f19 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/blackrock_depths.cpp @@ -0,0 +1,237 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Blackrock_Depths +SD%Complete: 95 +SDComment: Quest support: 4001, 4342, 7604. Vendor Lokhtos Darkbargainer. +SDCategory: Blackrock Depths +EndScriptData */ + +/* ContentData +mob_phalanx +npc_kharan_mighthammer +npc_lokhtos_darkbargainer +EndContentData */ + +#include "precompiled.h" + +/*###### +## mob_phalanx +######*/ + +#define SPELL_THUNDERCLAP 8732 +#define SPELL_FIREBALLVOLLEY 22425 +#define SPELL_MIGHTYBLOW 14099 + +struct MANGOS_DLL_DECL mob_phalanxAI : public ScriptedAI +{ + mob_phalanxAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ThunderClap_Timer; + uint32 FireballVolley_Timer; + uint32 MightyBlow_Timer; + + void Reset() + { + ThunderClap_Timer = 12000; + FireballVolley_Timer =0; + MightyBlow_Timer = 15000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ThunderClap_Timer + if( ThunderClap_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_THUNDERCLAP); + ThunderClap_Timer = 10000; + }else ThunderClap_Timer -= diff; + + //FireballVolley_Timer + if( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 51 ) + { + if (FireballVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIREBALLVOLLEY); + FireballVolley_Timer = 15000; + }else FireballVolley_Timer -= diff; + } + + //MightyBlow_Timer + if( MightyBlow_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_MIGHTYBLOW); + MightyBlow_Timer = 10000; + }else MightyBlow_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_mob_phalanx(Creature *_Creature) +{ + return new mob_phalanxAI (_Creature); +} + +/*###### +## npc_kharan_mighthammer +######*/ + +#define QUEST_4001 4001 +#define QUEST_4342 4342 + +bool GossipHello_npc_kharan_mighthammer(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( player->GetQuestStatus(QUEST_4001) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM( 0, "I need to know where the princess are, Kharan!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + if( player->GetQuestStatus(4342) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM( 0, "All is not lost, Kharan!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + + if( player->GetTeam() == HORDE ) player->SEND_GOSSIP_MENU(2473, _Creature->GetGUID()); + if( player->GetTeam() == ALLIANCE ) player->SEND_GOSSIP_MENU(2474, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_kharan_mighthammer(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM(0, "Gor'shak is my friend, you can trust me.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(2475, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM(0, "Not enough, you need to tell me more.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->SEND_GOSSIP_MENU(2476, _Creature->GetGUID()); + break; + + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM(0, "So what happened?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + player->SEND_GOSSIP_MENU(2477, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM(0, "Continue...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(2478, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->ADD_GOSSIP_ITEM(0, "So you suspect that someone on the inside was involved? That they were tipped off?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+6); + player->SEND_GOSSIP_MENU(2479, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + player->ADD_GOSSIP_ITEM(0, "Continue with your story please.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+7); + player->SEND_GOSSIP_MENU(2480, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+7: + player->ADD_GOSSIP_ITEM(0, "Indeed.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+8); + player->SEND_GOSSIP_MENU(2481, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+8: + player->ADD_GOSSIP_ITEM(0, "The door is open, Kharan. You are a free man.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+9); + player->SEND_GOSSIP_MENU(2482, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+9: + player->CLOSE_GOSSIP_MENU(); + if( player->GetTeam() == HORDE ) player->AreaExploredOrEventHappens(QUEST_4001); + if( player->GetTeam() == ALLIANCE ) player->AreaExploredOrEventHappens(QUEST_4342); + break; + } + return true; +} + +/*###### +## npc_lokhtos_darkbargainer +######*/ + +#define ITEM_THRORIUM_BROTHERHOOD_CONTRACT 18628 +#define ITEM_SULFURON_INGOT 17203 +#define QUEST_A_BINDING_CONTRACT 7604 +#define SPELL_CREATE_THORIUM_BROTHERHOOD_CONTRACT_DND 23059 + +bool GossipHello_npc_lokhtos_darkbargainer(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (_Creature->isVendor() && player->GetReputationRank(59) >= REP_FRIENDLY) + player->ADD_GOSSIP_ITEM( 1, "Show me what I have access to, Lothos.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + + if (player->GetQuestRewardStatus(QUEST_A_BINDING_CONTRACT) != 1 && + !player->HasItemCount(ITEM_THRORIUM_BROTHERHOOD_CONTRACT, 1, true) && + player->HasItemCount(ITEM_SULFURON_INGOT, 1)) + { + player->ADD_GOSSIP_ITEM(0, "Get Thorium Brotherhood Contract", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + } + + if (player->GetReputationRank(59) < REP_FRIENDLY) + player->SEND_GOSSIP_MENU(3673, _Creature->GetGUID()); + else + player->SEND_GOSSIP_MENU(3677, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_lokhtos_darkbargainer(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player, SPELL_CREATE_THORIUM_BROTHERHOOD_CONTRACT_DND, false); + } + if (action == GOSSIP_ACTION_TRADE) + { + player->SEND_VENDORLIST( _Creature->GetGUID() ); + } + return true; +} + +/*###### +## +######*/ + +void AddSC_blackrock_depths() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="phalanx"; + newscript->GetAI = GetAI_mob_phalanx; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_kharan_mighthammer"; + newscript->pGossipHello = &GossipHello_npc_kharan_mighthammer; + newscript->pGossipSelect = &GossipSelect_npc_kharan_mighthammer; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_lokhtos_darkbargainer"; + newscript->pGossipHello = &GossipHello_npc_lokhtos_darkbargainer; + newscript->pGossipSelect = &GossipSelect_npc_lokhtos_darkbargainer; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_ambassador_flamelash.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_ambassador_flamelash.cpp new file mode 100644 index 00000000000..613da72ef3a --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_ambassador_flamelash.cpp @@ -0,0 +1,106 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Ambassador_Flamelash +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FIREBLAST 15573 + +struct MANGOS_DLL_DECL boss_ambassador_flamelashAI : public ScriptedAI +{ + boss_ambassador_flamelashAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FireBlast_Timer; + uint32 Spirit_Timer; + int Rand; + int RandX; + int RandY; + Creature* Summoned; + + void Reset() + { + FireBlast_Timer = 2000; + Spirit_Timer = 24000; + } + + void Aggro(Unit *who) {} + + void SummonSpirits(Unit* victim) + { + Rand = rand()%10; + switch (rand()%2) + { + case 0: RandX -= Rand; break; + case 1: RandX += Rand; break; + } + Rand = 0; + Rand = rand()%10; + switch (rand()%2) + { + case 0: RandY -= Rand; break; + case 1: RandY += Rand; break; + } + Summoned = DoSpawnCreature(9178, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); + if(Summoned) + ((CreatureAI*)Summoned->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //FireBlast_Timer + if (FireBlast_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIREBLAST); + FireBlast_Timer = 7000; + }else FireBlast_Timer -= diff; + + //Spirit_Timer + if (Spirit_Timer < diff) + { + SummonSpirits(m_creature->getVictim()); + SummonSpirits(m_creature->getVictim()); + SummonSpirits(m_creature->getVictim()); + SummonSpirits(m_creature->getVictim()); + + Spirit_Timer = 30000; + }else Spirit_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_ambassador_flamelash(Creature *_Creature) +{ + return new boss_ambassador_flamelashAI (_Creature); +} + +void AddSC_boss_ambassador_flamelash() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_ambassador_flamelash"; + newscript->GetAI = GetAI_boss_ambassador_flamelash; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_angerrel.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_angerrel.cpp new file mode 100644 index 00000000000..98e382ae233 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_angerrel.cpp @@ -0,0 +1,91 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Angerrel +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SUNDERARMOR 24317 +#define SPELL_SHIELDBLOCK 12169 +#define SPELL_STRIKE 15580 + +struct MANGOS_DLL_DECL boss_angerrelAI : public ScriptedAI +{ + boss_angerrelAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 SunderArmor_Timer; + uint32 ShieldBlock_Timer; + uint32 Strike_Timer; + + void Reset() + { + SunderArmor_Timer = 8000; + ShieldBlock_Timer = 15000; + Strike_Timer = 12000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //SunderArmor_Timer + if (SunderArmor_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SUNDERARMOR); + SunderArmor_Timer = 28000; + }else SunderArmor_Timer -= diff; + + //ShieldBlock_Timer + if (ShieldBlock_Timer < diff) + { + DoCast(m_creature,SPELL_SHIELDBLOCK); + ShieldBlock_Timer = 25000; + }else ShieldBlock_Timer -= diff; + + //Strike_Timer + if (Strike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_STRIKE); + Strike_Timer = 10000; + }else Strike_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_angerrel(Creature *_Creature) +{ + return new boss_angerrelAI (_Creature); +} + +void AddSC_boss_angerrel() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_angerrel"; + newscript->GetAI = GetAI_boss_angerrel; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_anubshiah.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_anubshiah.cpp new file mode 100644 index 00000000000..1cf80bfc8ec --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_anubshiah.cpp @@ -0,0 +1,115 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Anubshiah +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWBOLT 17228 +#define SPELL_CURSEOFTONGUES 15470 +#define SPELL_CURSEOFWEAKNESS 17227 +#define SPELL_DEMONARMOR 11735 +#define SPELL_ENVELOPINGWEB 15471 + +struct MANGOS_DLL_DECL boss_anubshiahAI : public ScriptedAI +{ + boss_anubshiahAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowBolt_Timer; + uint32 CurseOfTongues_Timer; + uint32 CurseOfWeakness_Timer; + uint32 DemonArmor_Timer; + uint32 EnvelopingWeb_Timer; + + void Reset() + { + ShadowBolt_Timer = 7000; + CurseOfTongues_Timer = 24000; + CurseOfWeakness_Timer = 12000; + DemonArmor_Timer = 3000; + EnvelopingWeb_Timer = 16000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ShadowBolt_Timer + if (ShadowBolt_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWBOLT); + ShadowBolt_Timer = 7000; + }else ShadowBolt_Timer -= diff; + + //CurseOfTongues_Timer + if (CurseOfTongues_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_CURSEOFTONGUES); + CurseOfTongues_Timer = 18000; + }else CurseOfTongues_Timer -= diff; + + //CurseOfWeakness_Timer + if (CurseOfWeakness_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CURSEOFWEAKNESS); + CurseOfWeakness_Timer = 45000; + }else CurseOfWeakness_Timer -= diff; + + //DemonArmor_Timer + if (DemonArmor_Timer < diff) + { + DoCast(m_creature,SPELL_DEMONARMOR); + DemonArmor_Timer = 300000; + }else DemonArmor_Timer -= diff; + + //EnvelopingWeb_Timer + if (EnvelopingWeb_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_ENVELOPINGWEB); + EnvelopingWeb_Timer = 12000; + }else EnvelopingWeb_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_anubshiah(Creature *_Creature) +{ + return new boss_anubshiahAI (_Creature); +} + +void AddSC_boss_anubshiah() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_anubshiah"; + newscript->GetAI = GetAI_boss_anubshiah; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_doomrel.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_doomrel.cpp new file mode 100644 index 00000000000..2f70b4ee14c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_doomrel.cpp @@ -0,0 +1,139 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Doomrel +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWBOLTVOLLEY 17228 +#define SPELL_IMMOLATE 15505 +#define SPELL_CURSEOFWEAKNESS 17227 +#define SPELL_DEMONARMOR 11735 + +struct MANGOS_DLL_DECL boss_doomrelAI : public ScriptedAI +{ + boss_doomrelAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowVolley_Timer; + uint32 Immolate_Timer; + uint32 CurseOfWeakness_Timer; + uint32 DemonArmor_Timer; + bool Voidwalkers; + int Rand; + int RandX; + int RandY; + Creature* Summoned; + + void Reset() + { + ShadowVolley_Timer = 10000; + Immolate_Timer = 18000; + CurseOfWeakness_Timer = 5000; + DemonArmor_Timer = 16000; + Voidwalkers = false; + } + + void Aggro(Unit *who) + { + } + + void SummonVoidwalkers(Unit* victim) + { + Rand = rand()%5; + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = rand()%5; + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + Summoned = DoSpawnCreature(16119, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 120000); + if(Summoned) + ((CreatureAI*)Summoned->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ShadowVolley_Timer + if (ShadowVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWBOLTVOLLEY); + ShadowVolley_Timer = 12000; + }else ShadowVolley_Timer -= diff; + + //Immolate_Timer + if (Immolate_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target)DoCast(target,SPELL_IMMOLATE); + Immolate_Timer = 25000; + }else Immolate_Timer -= diff; + + //CurseOfWeakness_Timer + if (CurseOfWeakness_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CURSEOFWEAKNESS); + CurseOfWeakness_Timer = 45000; + }else CurseOfWeakness_Timer -= diff; + + //DemonArmor_Timer + if (DemonArmor_Timer < diff) + { + DoCast(m_creature,SPELL_DEMONARMOR); + DemonArmor_Timer = 300000; + }else DemonArmor_Timer -= diff; + + //Summon Voidwalkers + if (!Voidwalkers && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 51 ) + { + SummonVoidwalkers(m_creature->getVictim()); + SummonVoidwalkers(m_creature->getVictim()); + SummonVoidwalkers(m_creature->getVictim()); + Voidwalkers = true; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_doomrel(Creature *_Creature) +{ + return new boss_doomrelAI (_Creature); +} + +void AddSC_boss_doomrel() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_doomrel"; + newscript->GetAI = GetAI_boss_doomrel; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_doperel.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_doperel.cpp new file mode 100644 index 00000000000..3aae6cb1adf --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_doperel.cpp @@ -0,0 +1,91 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Doperel +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SINISTERSTRIKE 15581 +#define SPELL_BACKSTAB 15582 +#define SPELL_GOUGE 13579 + +struct MANGOS_DLL_DECL boss_doperelAI : public ScriptedAI +{ + boss_doperelAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 SinisterStrike_Timer; + uint32 BackStab_Timer; + uint32 Gouge_Timer; + + void Reset() + { + SinisterStrike_Timer = 8000; + BackStab_Timer = 12000; + Gouge_Timer = 6000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //SinisterStrike_Timer + if (SinisterStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SINISTERSTRIKE); + SinisterStrike_Timer = 7000; + }else SinisterStrike_Timer -= diff; + + //BackStab_Timer + if (BackStab_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_BACKSTAB); + BackStab_Timer = 6000; + }else BackStab_Timer -= diff; + + //Gouge_Timer + if (Gouge_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_GOUGE); + Gouge_Timer = 8000; + }else Gouge_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_doperel(Creature *_Creature) +{ + return new boss_doperelAI (_Creature); +} + +void AddSC_boss_doperel() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_doperel"; + newscript->GetAI = GetAI_boss_doperel; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_emperor_dagran_thaurissan.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_emperor_dagran_thaurissan.cpp new file mode 100644 index 00000000000..cd03b6c1894 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_emperor_dagran_thaurissan.cpp @@ -0,0 +1,104 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Emperor_Dagran_Thaurissan +SD%Complete: 99 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_HANDOFTHAURISSAN 17492 +#define SPELL_AVATAROFFLAME 15636 + +#define SAY_AGGRO "Come to aid the Throne!" +#define SAY_SLAY "Hail to the king, baby!" + +struct MANGOS_DLL_DECL boss_draganthaurissanAI : public ScriptedAI +{ + boss_draganthaurissanAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 HandOfThaurissan_Timer; + uint32 AvatarOfFlame_Timer; + //uint32 Counter; + + void Reset() + { + HandOfThaurissan_Timer = 4000; + AvatarOfFlame_Timer = 25000; + //Counter= 0; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + } + + void KilledUnit(Unit* victim) + { + DoYell(SAY_SLAY, LANG_UNIVERSAL, NULL); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if (HandOfThaurissan_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_HANDOFTHAURISSAN); + + //3 Hands of Thaurissan will be casted + //if (Counter < 3) + //{ + // HandOfThaurissan_Timer = 1000; + // Counter++; + //} + //else + //{ + HandOfThaurissan_Timer = 5000; + //Counter=0; + //} + }else HandOfThaurissan_Timer -= diff; + + //AvatarOfFlame_Timer + if (AvatarOfFlame_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_AVATAROFFLAME); + AvatarOfFlame_Timer = 18000; + }else AvatarOfFlame_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_draganthaurissan(Creature *_Creature) +{ + return new boss_draganthaurissanAI (_Creature); +} + +void AddSC_boss_draganthaurissan() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_emperor_dagran_thaurissan"; + newscript->GetAI = GetAI_boss_draganthaurissan; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_general_angerforge.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_general_angerforge.cpp new file mode 100644 index 00000000000..2d481bdaeb0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_general_angerforge.cpp @@ -0,0 +1,167 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_General_Angerforge +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_MIGHTYBLOW 14099 +#define SPELL_HAMSTRING 9080 +#define SPELL_CLEAVE 20691 + +struct MANGOS_DLL_DECL boss_general_angerforgeAI : public ScriptedAI +{ + boss_general_angerforgeAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 MightyBlow_Timer; + uint32 HamString_Timer; + uint32 Cleave_Timer; + uint32 Adds_Timer; + bool Medics; + int Rand1; + int Rand1X; + int Rand1Y; + int Rand2; + int Rand2X; + int Rand2Y; + Creature* SummonedAdds; + Creature* SummonedMedics; + + void Reset() + { + MightyBlow_Timer = 8000; + HamString_Timer = 12000; + Cleave_Timer = 16000; + Adds_Timer = 0; + Medics = false; + } + + void Aggro(Unit *who) + { + } + + void SummonAdds(Unit* victim) + { + Rand1 = rand()%15; + switch (rand()%2) + { + case 0: Rand1X = 0 - Rand1; break; + case 1: Rand1X = 0 + Rand1; break; + } + Rand1 = 0; + Rand1 = rand()%15; + switch (rand()%2) + { + case 0: Rand1Y = 0 - Rand1; break; + case 1: Rand1Y = 0 + Rand1; break; + } + Rand1 = 0; + SummonedAdds = DoSpawnCreature(8901, Rand1X, Rand1Y, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 120000); + if(SummonedAdds) + ((CreatureAI*)SummonedAdds->AI())->AttackStart(victim); + } + + void SummonMedics(Unit* victim) + { + Rand2 = rand()%10; + switch (rand()%2) + { + case 0: Rand2X = 0 - Rand2; break; + case 1: Rand2X = 0 + Rand2; break; + } + Rand2 = 0; + Rand2 = rand()%10; + switch (rand()%2) + { + case 0: Rand2Y = 0 - Rand2; break; + case 1: Rand2Y = 0 + Rand2; break; + } + Rand2 = 0; + SummonedMedics = DoSpawnCreature(8894, Rand2X, Rand2Y, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 120000); + if(SummonedMedics) + ((CreatureAI*)SummonedMedics->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //MightyBlow_Timer + if (MightyBlow_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MIGHTYBLOW); + MightyBlow_Timer = 18000; + }else MightyBlow_Timer -= diff; + + //HamString_Timer + if (HamString_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HAMSTRING); + HamString_Timer = 15000; + }else HamString_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 9000; + }else Cleave_Timer -= diff; + + //Adds_Timer + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 21 ) + { + if (Adds_Timer < diff) + { + // summon 3 Adds every 25s + SummonAdds(m_creature->getVictim()); + SummonAdds(m_creature->getVictim()); + SummonAdds(m_creature->getVictim()); + + Adds_Timer = 25000; + } else Adds_Timer -= diff; + } + + //Summon Medics + if ( !Medics && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 21 ) + { + SummonMedics(m_creature->getVictim()); + SummonMedics(m_creature->getVictim()); + Medics = true; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_general_angerforge(Creature *_Creature) +{ + return new boss_general_angerforgeAI (_Creature); +} + +void AddSC_boss_general_angerforge() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_general_angerforge"; + newscript->GetAI = GetAI_boss_general_angerforge; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_gloomrel.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_gloomrel.cpp new file mode 100644 index 00000000000..cb87855ee53 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_gloomrel.cpp @@ -0,0 +1,142 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Gloomrel +SD%Complete: 80 +SDComment: Learning Smelt Dark Iron if tribute quest rewarded. Missing event and re-spawn GO Spectral Chalice +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_HAMSTRING 9080 +#define SPELL_CLEAVE 15579 +#define SPELL_MORTALSTRIKE 15708 + +struct MANGOS_DLL_DECL boss_gloomrelAI : public ScriptedAI +{ + boss_gloomrelAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 HamString_Timer; + uint32 Cleave_Timer; + uint32 MortalStrike_Timer; + + void Reset() + { + HamString_Timer = 19000; + Cleave_Timer = 6000; + MortalStrike_Timer = 10000; + + m_creature->setFaction(734); + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //HamString_Timer + if (HamString_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HAMSTRING); + HamString_Timer = 14000; + }else HamString_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 8000; + }else Cleave_Timer -= diff; + + //MortalStrike_Timer + if (MortalStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MORTALSTRIKE); + MortalStrike_Timer = 12000; + }else MortalStrike_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_gloomrel(Creature *_Creature) +{ + return new boss_gloomrelAI (_Creature); +} + +bool GossipHello_boss_gloomrel(Player *player, Creature *_Creature) +{ + if (player->GetQuestRewardStatus(4083) == 1 && player->GetSkillValue(SKILL_MINING) >= 230 && !player->HasSpell(14891) ) + player->ADD_GOSSIP_ITEM(0, "Teach me the art of smelting dark iron", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + if (player->GetQuestRewardStatus(4083) == 0 && player->GetSkillValue(SKILL_MINING) >= 230) + player->ADD_GOSSIP_ITEM(0, "I want to pay tribute", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + + player->ADD_GOSSIP_ITEM(0, "Challenge", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(2602, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_boss_gloomrel(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "Continue...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(2606, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+11: + player->CLOSE_GOSSIP_MENU(); + _Creature->CastSpell(player, 14894, false); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "[PH] Continue...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 22); + player->SEND_GOSSIP_MENU(2604, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+22: + player->CLOSE_GOSSIP_MENU(); + //re-spawn object here + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, "[PH] Continue...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 33); + player->SEND_GOSSIP_MENU(2605, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+33: + player->CLOSE_GOSSIP_MENU(); + //start event here, below code just temporary + _Creature->setFaction(754); + break; + } + return true; +} + +void AddSC_boss_gloomrel() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_gloomrel"; + newscript->GetAI = GetAI_boss_gloomrel; + newscript->pGossipHello = &GossipHello_boss_gloomrel; + newscript->pGossipSelect = &GossipSelect_boss_gloomrel; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_gorosh_the_dervish.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_gorosh_the_dervish.cpp new file mode 100644 index 00000000000..df67093e61f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_gorosh_the_dervish.cpp @@ -0,0 +1,81 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Gorosh_the_Dervish +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_WHIRLWIND 15589 +#define SPELL_MORTALSTRIKE 24573 + +struct MANGOS_DLL_DECL boss_gorosh_the_dervishAI : public ScriptedAI +{ + boss_gorosh_the_dervishAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 WhirlWind_Timer; + uint32 MortalStrike_Timer; + + void Reset() + { + WhirlWind_Timer = 12000; + MortalStrike_Timer = 22000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //WhirlWind_Timer + if (WhirlWind_Timer < diff) + { + DoCast(m_creature,SPELL_WHIRLWIND); + WhirlWind_Timer = 15000; + }else WhirlWind_Timer -= diff; + + //MortalStrike_Timer + if (MortalStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MORTALSTRIKE); + MortalStrike_Timer = 15000; + }else MortalStrike_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_gorosh_the_dervish(Creature *_Creature) +{ + return new boss_gorosh_the_dervishAI (_Creature); +} + +void AddSC_boss_gorosh_the_dervish() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_gorosh_the_dervish"; + newscript->GetAI = GetAI_boss_gorosh_the_dervish; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_grizzle.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_grizzle.cpp new file mode 100644 index 00000000000..7e248c2d17b --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_grizzle.cpp @@ -0,0 +1,86 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Grizzle +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_GROUNDTREMOR 6524 +#define SPELL_FRENZY 28371 + +struct MANGOS_DLL_DECL boss_grizzleAI : public ScriptedAI +{ + boss_grizzleAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 GroundTremor_Timer; + uint32 Frenzy_Timer; + + void Reset() + { + GroundTremor_Timer = 12000; + Frenzy_Timer =0; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //GroundTremor_Timer + if (GroundTremor_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_GROUNDTREMOR); + GroundTremor_Timer = 8000; + }else GroundTremor_Timer -= diff; + + //Frenzy_Timer + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 51 ) + { + if (Frenzy_Timer < diff) + { + DoCast(m_creature,SPELL_FRENZY); + DoTextEmote("goes into a killing frenzy!",NULL); + + Frenzy_Timer = 15000; + }else Frenzy_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_grizzle(Creature *_Creature) +{ + return new boss_grizzleAI (_Creature); +} + +void AddSC_boss_grizzle() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_grizzle"; + newscript->GetAI = GetAI_boss_grizzle; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_haterel.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_haterel.cpp new file mode 100644 index 00000000000..1392eb54943 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_haterel.cpp @@ -0,0 +1,105 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Haterel +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWBOLT 17483 //Not sure if right ID +#define SPELL_MANABURN 10876 +#define SPELL_SHADOWSHIELD 22417 +#define SPELL_STRIKE 15580 + +struct MANGOS_DLL_DECL boss_haterelAI : public ScriptedAI +{ + boss_haterelAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowBolt_Timer; + uint32 ManaBurn_Timer; + uint32 ShadowShield_Timer; + uint32 Strike_Timer; + + void Reset() + { + ShadowBolt_Timer = 15000; + ManaBurn_Timer = 3000; + ShadowShield_Timer = 8000; + Strike_Timer = 12000; + } + + void Aggro(Unit *who) + { + } + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ShadowBolt_Timer + if (ShadowBolt_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_SHADOWBOLT); + ShadowBolt_Timer = 7000; + }else ShadowBolt_Timer -= diff; + + //ManaBurn_Timer + if (ManaBurn_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_MANABURN); + ManaBurn_Timer = 13000; + }else ManaBurn_Timer -= diff; + + //ShadowShield_Timer + if (ShadowShield_Timer < diff) + { + DoCast(m_creature,SPELL_SHADOWSHIELD); + ShadowShield_Timer = 25000; + }else ShadowShield_Timer -= diff; + + //Strike_Timer + if (Strike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_STRIKE); + Strike_Timer = 10000; + }else Strike_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_haterel(Creature *_Creature) +{ + return new boss_haterelAI (_Creature); +} + +void AddSC_boss_haterel() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_haterel"; + newscript->GetAI = GetAI_boss_haterel; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_high_interrogator_gerstahn.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_high_interrogator_gerstahn.cpp new file mode 100644 index 00000000000..c4393560d10 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_high_interrogator_gerstahn.cpp @@ -0,0 +1,105 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_High_Interrogator_Gerstahn +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWWORDPAIN 10894 +#define SPELL_MANABURN 10876 +#define SPELL_PSYCHICSCREAM 8122 +#define SPELL_SHADOWSHIELD 22417 + +struct MANGOS_DLL_DECL boss_high_interrogator_gerstahnAI : public ScriptedAI +{ + boss_high_interrogator_gerstahnAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowWordPain_Timer; + uint32 ManaBurn_Timer; + uint32 PsychicScream_Timer; + uint32 ShadowShield_Timer; + + void Reset() + { + ShadowWordPain_Timer = 4000; + ManaBurn_Timer = 14000; + PsychicScream_Timer = 32000; + ShadowShield_Timer = 8000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ShadowWordPain_Timer + if (ShadowWordPain_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target)DoCast(target,SPELL_SHADOWWORDPAIN); + ShadowWordPain_Timer = 7000; + }else ShadowWordPain_Timer -= diff; + + //ManaBurn_Timer + if (ManaBurn_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target)DoCast(target,SPELL_MANABURN); + ManaBurn_Timer = 10000; + }else ManaBurn_Timer -= diff; + + //PsychicScream_Timer + if (PsychicScream_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_PSYCHICSCREAM); + PsychicScream_Timer = 30000; + }else PsychicScream_Timer -= diff; + + //ShadowShield_Timer + if (ShadowShield_Timer < diff) + { + DoCast(m_creature,SPELL_SHADOWSHIELD); + ShadowShield_Timer = 25000; + }else ShadowShield_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_high_interrogator_gerstahn(Creature *_Creature) +{ + return new boss_high_interrogator_gerstahnAI (_Creature); +} + +void AddSC_boss_high_interrogator_gerstahn() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_high_interrogator_gerstahn"; + newscript->GetAI = GetAI_boss_high_interrogator_gerstahn; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_magmus.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_magmus.cpp new file mode 100644 index 00000000000..62d2f6d8147 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_magmus.cpp @@ -0,0 +1,84 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Magmus +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FIERYBURST 13900 +#define SPELL_WARSTOMP 24375 + +struct MANGOS_DLL_DECL boss_magmusAI : public ScriptedAI +{ + boss_magmusAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FieryBurst_Timer; + uint32 WarStomp_Timer; + + void Reset() + { + FieryBurst_Timer = 5000; + WarStomp_Timer =0; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //FieryBurst_Timer + if (FieryBurst_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIERYBURST); + FieryBurst_Timer = 6000; + }else FieryBurst_Timer -= diff; + + //WarStomp_Timer + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 51 ) + { + if (WarStomp_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_WARSTOMP); + WarStomp_Timer = 8000; + }else WarStomp_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_magmus(Creature *_Creature) +{ + return new boss_magmusAI (_Creature); +} + +void AddSC_boss_magmus() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_magmus"; + newscript->GetAI = GetAI_boss_magmus; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_moira_bronzebeard.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_moira_bronzebeard.cpp new file mode 100644 index 00000000000..b7dfadd385d --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_moira_bronzebeard.cpp @@ -0,0 +1,99 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Moira_Bronzbeard +SD%Complete: 90 +SDComment: Healing of Emperor NYI +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_HEAL 10917 +#define SPELL_RENEW 10929 +#define SPELL_SHIELD 10901 +#define SPELL_MINDBLAST 10947 +#define SPELL_SHADOWWORDPAIN 10894 +#define SPELL_SMITE 10934 + +struct MANGOS_DLL_DECL boss_moira_bronzebeardAI : public ScriptedAI +{ + boss_moira_bronzebeardAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Heal_Timer; + uint32 MindBlast_Timer; + uint32 ShadowWordPain_Timer; + uint32 Smite_Timer; + Unit* PlayerHolder; + Unit* Target; + bool Heal; + + void Reset() + { + Target = NULL; + Heal_Timer = 12000; //These times are probably wrong + MindBlast_Timer = 16000; + ShadowWordPain_Timer = 2000; + Smite_Timer = 8000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //MindBlast_Timer + if (MindBlast_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MINDBLAST); + MindBlast_Timer = 14000; + }else MindBlast_Timer -= diff; + + //ShadowWordPain_Timer + if (ShadowWordPain_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWWORDPAIN); + ShadowWordPain_Timer = 18000; + }else ShadowWordPain_Timer -= diff; + + //Smite_Timer + if (Smite_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SMITE); + Smite_Timer = 10000; + }else Smite_Timer -= diff; + + } +}; +CreatureAI* GetAI_boss_moira_bronzebeard(Creature *_Creature) +{ + return new boss_moira_bronzebeardAI (_Creature); +} + +void AddSC_boss_moira_bronzebeard() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_moira_bronzebeard"; + newscript->GetAI = GetAI_boss_moira_bronzebeard; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_seethrel.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_seethrel.cpp new file mode 100644 index 00000000000..f1d703924fb --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_seethrel.cpp @@ -0,0 +1,115 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Seethrel +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FROSTBOLT 16799 +#define SPELL_FROSTARMOR 15784 //This is actually a buff he gives himself +#define SPELL_BLIZZARD 19099 +#define SPELL_FROSTNOVA 15063 +#define SPELL_FROSTWARD 15004 + +struct MANGOS_DLL_DECL boss_seethrelAI : public ScriptedAI +{ + boss_seethrelAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FrostArmor_Timer; + uint32 Frostbolt_Timer; + uint32 Blizzard_Timer; + uint32 FrostNova_Timer; + uint32 FrostWard_Timer; + + void Reset() + { + FrostArmor_Timer = 2000; + Frostbolt_Timer = 6000; + Blizzard_Timer = 18000; + FrostNova_Timer = 12000; + FrostWard_Timer = 25000; + + m_creature->CastSpell(m_creature,SPELL_FROSTARMOR,true); + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //FrostArmor_Timer + if (FrostArmor_Timer < diff) + { + DoCast(m_creature, SPELL_FROSTARMOR); + FrostArmor_Timer = 180000; + }else FrostArmor_Timer -= diff; + + //Frostbolt_Timer + if (Frostbolt_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROSTBOLT); + Frostbolt_Timer = 15000; + }else Frostbolt_Timer -= diff; + + //Blizzard_Timer + if (Blizzard_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_BLIZZARD); + Blizzard_Timer = 22000; + }else Blizzard_Timer -= diff; + + //FrostNova_Timer + if (FrostNova_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROSTNOVA); + FrostNova_Timer = 14000; + }else FrostNova_Timer -= diff; + + //FrostWard_Timer + if (FrostWard_Timer < diff) + { + DoCast(m_creature,SPELL_FROSTWARD); + FrostWard_Timer = 68000; + }else FrostWard_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_seethrel(Creature *_Creature) +{ + return new boss_seethrelAI (_Creature); +} + +void AddSC_boss_seethrel() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_seethrel"; + newscript->GetAI = GetAI_boss_seethrel; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_depths/boss_vilerel.cpp b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_vilerel.cpp new file mode 100644 index 00000000000..2c7508a0311 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_depths/boss_vilerel.cpp @@ -0,0 +1,101 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Vilerel +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Depths +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_MINDBLAST 15587 +#define SPELL_HEAL 15586 +#define SPELL_PRAYEROFHEALING 15585 +#define SPELL_SHIELD 10901 + +struct MANGOS_DLL_DECL boss_vilerelAI : public ScriptedAI +{ + boss_vilerelAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 MindBlast_Timer; + uint32 Heal_Timer; + uint32 PrayerOfHealing_Timer; + uint32 Shield_Timer; + + void Reset() + { + MindBlast_Timer = 10000; + Heal_Timer = 35000; + PrayerOfHealing_Timer = 25000; + Shield_Timer = 3000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //MindBlast_Timer + if (MindBlast_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MINDBLAST); + MindBlast_Timer = 7000; + }else MindBlast_Timer -= diff; + + //Heal_Timer + if (Heal_Timer < diff) + { + DoCast(m_creature,SPELL_HEAL); + Heal_Timer = 20000; + }else Heal_Timer -= diff; + + //PrayerOfHealing_Timer + if (PrayerOfHealing_Timer < diff) + { + DoCast(m_creature,SPELL_PRAYEROFHEALING); + PrayerOfHealing_Timer = 30000; + }else PrayerOfHealing_Timer -= diff; + + //Shield_Timer + if (Shield_Timer < diff) + { + DoCast(m_creature,SPELL_SHIELD); + Shield_Timer = 30000; + }else Shield_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_vilerel(Creature *_Creature) +{ + return new boss_vilerelAI (_Creature); +} + +void AddSC_boss_vilerel() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_vilerel"; + newscript->GetAI = GetAI_boss_vilerel; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_drakkisath.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_drakkisath.cpp new file mode 100644 index 00000000000..a69334aeac8 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_drakkisath.cpp @@ -0,0 +1,101 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Drakkisath +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FIRENOVA 23462 +#define SPELL_CLEAVE 20691 +#define SPELL_CONFLIGURATION 16805 +#define SPELL_THUNDERCLAP 15548 //Not sure if right ID. 23931 would be a harder possibility. + +struct MANGOS_DLL_DECL boss_drakkisathAI : public ScriptedAI +{ + boss_drakkisathAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FireNova_Timer; + uint32 Cleave_Timer; + uint32 Confliguration_Timer; + uint32 Thunderclap_Timer; + + void Reset() + { + FireNova_Timer = 6000; + Cleave_Timer = 8000; + Confliguration_Timer = 15000; + Thunderclap_Timer = 17000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //FireNova_Timer + if (FireNova_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIRENOVA); + FireNova_Timer = 10000; + }else FireNova_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 8000; + }else Cleave_Timer -= diff; + + //Confliguration_Timer + if (Confliguration_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CONFLIGURATION); + Confliguration_Timer = 18000; + }else Confliguration_Timer -= diff; + + //Thunderclap_Timer + if (Thunderclap_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_THUNDERCLAP); + Thunderclap_Timer = 20000; + }else Thunderclap_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_drakkisath(Creature *_Creature) +{ + return new boss_drakkisathAI (_Creature); +} + +void AddSC_boss_drakkisath() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_drakkisath"; + newscript->GetAI = GetAI_boss_drakkisath; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_gyth.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_gyth.cpp new file mode 100644 index 00000000000..e2edaf7d07d --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_gyth.cpp @@ -0,0 +1,205 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Gyth +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_CORROSIVEACID 20667 +#define SPELL_FREEZE 18763 +#define SPELL_FLAMEBREATH 20712 + +struct MANGOS_DLL_DECL boss_gythAI : public ScriptedAI +{ + boss_gythAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Aggro_Timer; + uint32 Dragons_Timer; + uint32 Orc_Timer; + uint32 CorrosiveAcid_Timer; + uint32 Freeze_Timer; + uint32 Flamebreath_Timer; + uint32 Line1Count; + uint32 Line2Count; + + bool Event; + bool SummonedDragons; + bool SummonedOrcs; + bool SummonedRend; + bool bAggro; + bool RootSelf; + Creature *SummonedCreature; + + void Reset() + { + Dragons_Timer = 3000; + Orc_Timer = 60000; + Aggro_Timer = 60000; + CorrosiveAcid_Timer = 8000; + Freeze_Timer = 11000; + Flamebreath_Timer = 4000; + Event = false; + SummonedDragons = false; + SummonedOrcs= false; + SummonedRend = false; + bAggro = false; + RootSelf = false; + + // how many times should the two lines of summoned creatures be spawned + // min 2 x 2, max 7 lines of attack in total + Line1Count = rand() % 4 + 2; + if (Line1Count < 5) + Line2Count = rand() % (5 - Line1Count) + 2; + else + Line2Count = 2; + + //Invisible for event start + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, 11686); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit *who) + { + } + + void SummonCreatureWithRandomTarget(uint32 creatureId) + { + Unit* Summoned = m_creature->SummonCreature(creatureId, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 240000); + if (Summoned) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (target) + Summoned->AddThreat(target, 1.0f); + } + } + + void UpdateAI(const uint32 diff) + { + //char buf[200]; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if (!RootSelf) + { + //m_creature->m_canMove = true; + DoCast(m_creature, 33356); + RootSelf = true; + } + + if (!bAggro && Line1Count == 0 && Line2Count == 0) + { + if (Aggro_Timer < diff) + { + bAggro = true; + // Visible now! + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, 9723); + m_creature->setFaction(14); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } else Aggro_Timer -= diff; + } + + // Summon Dragon pack. 2 Dragons and 3 Whelps + if (!bAggro && !SummonedRend && Line1Count > 0) + { + if (Dragons_Timer < diff) + { + SummonCreatureWithRandomTarget(10372); + SummonCreatureWithRandomTarget(10372); + SummonCreatureWithRandomTarget(10442); + SummonCreatureWithRandomTarget(10442); + SummonCreatureWithRandomTarget(10442); + Line1Count = Line1Count - 1; + Dragons_Timer = 60000; + } else Dragons_Timer -= diff; + } + + //Summon Orc pack. 1 Orc Handler 1 Elite Dragonkin and 3 Whelps + if (!bAggro && !SummonedRend && Line1Count == 0 && Line2Count > 0) + { + if (Orc_Timer < diff) + { + SummonCreatureWithRandomTarget(10447); + SummonCreatureWithRandomTarget(10317); + SummonCreatureWithRandomTarget(10442); + SummonCreatureWithRandomTarget(10442); + SummonCreatureWithRandomTarget(10442); + Line2Count = Line2Count - 1; + Orc_Timer = 60000; + } else Orc_Timer -= diff; + } + + // we take part in the fight + if (bAggro) + { + // CorrosiveAcid_Timer + if (CorrosiveAcid_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CORROSIVEACID); + CorrosiveAcid_Timer = 7000; + } else CorrosiveAcid_Timer -= diff; + + // Freeze_Timer + if (Freeze_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FREEZE); + Freeze_Timer = 16000; + } else Freeze_Timer -= diff; + + // Flamebreath_Timer + if (Flamebreath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FLAMEBREATH); + Flamebreath_Timer = 10500; + } else Flamebreath_Timer -= diff; + + //Summon Rend + if (!SummonedRend && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 11 + && m_creature->GetHealth() > 0 ) + { + //summon Rend and Change model to normal Gyth + //Inturrupt any spell casting + m_creature->InterruptNonMeleeSpells(false); + //Gyth model + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, 9806); + m_creature->SummonCreature(10429, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 900000); + SummonedRend = true; + } + + DoMeleeAttackIfReady(); + } // end if Aggro + } +}; + +CreatureAI* GetAI_boss_gyth(Creature *_Creature) +{ + return new boss_gythAI (_Creature); +} + +void AddSC_boss_gyth() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_gyth"; + newscript->GetAI = GetAI_boss_gyth; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_halycon.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_halycon.cpp new file mode 100644 index 00000000000..27509b43355 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_halycon.cpp @@ -0,0 +1,95 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Halycon +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_CROWDPUMMEL 10887 +#define SPELL_MIGHTYBLOW 14099 + +#define ADD_1X -169.839203 +#define ADD_1Y -324.961395 +#define ADD_1Z 64.401443 +#define ADD_1O 3.124724 + +struct MANGOS_DLL_DECL boss_halyconAI : public ScriptedAI +{ + boss_halyconAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 CrowdPummel_Timer; + uint32 MightyBlow_Timer; + bool Summoned; + + void Reset() + { + CrowdPummel_Timer = 8000; + MightyBlow_Timer = 14000; + Summoned = false; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //CrowdPummel_Timer + if (CrowdPummel_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CROWDPUMMEL); + CrowdPummel_Timer = 14000; + }else CrowdPummel_Timer -= diff; + + //MightyBlow_Timer + if (MightyBlow_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MIGHTYBLOW); + MightyBlow_Timer = 10000; + }else MightyBlow_Timer -= diff; + + //Summon Gizrul + if ( !Summoned && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 25 ) + { + m_creature->SummonCreature(10268,ADD_1X,ADD_1Y,ADD_1Z,ADD_1O,TEMPSUMMON_TIMED_DESPAWN,300000); + Summoned = true; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_halycon(Creature *_Creature) +{ + return new boss_halyconAI (_Creature); +} + +void AddSC_boss_halycon() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_halycon"; + newscript->GetAI = GetAI_boss_halycon; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_highlord_omokk.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_highlord_omokk.cpp new file mode 100644 index 00000000000..b0fc6b3ac14 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_highlord_omokk.cpp @@ -0,0 +1,131 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Highlord_Omokk +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_WARSTOMP 24375 +#define SPELL_CLEAVE 15579 +#define SPELL_STRIKE 18368 +#define SPELL_REND 18106 +#define SPELL_SUNDERARMOR 24317 +#define SPELL_KNOCKAWAY 20686 +#define SPELL_SLOW 22356 + +struct MANGOS_DLL_DECL boss_highlordomokkAI : public ScriptedAI +{ + boss_highlordomokkAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 WarStomp_Timer; + uint32 Cleave_Timer; + uint32 Strike_Timer; + uint32 Rend_Timer; + uint32 SunderArmor_Timer; + uint32 KnockAway_Timer; + uint32 Slow_Timer; + + void Reset() + { + WarStomp_Timer = 15000; + Cleave_Timer = 6000; + Strike_Timer = 10000; + Rend_Timer = 14000; + SunderArmor_Timer = 2000; + KnockAway_Timer = 18000; + Slow_Timer = 24000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //WarStomp_Timer + if (WarStomp_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_WARSTOMP); + WarStomp_Timer = 14000; + }else WarStomp_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 8000; + }else Cleave_Timer -= diff; + + //Strike_Timer + if (Strike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_STRIKE); + Strike_Timer = 10000; + }else Strike_Timer -= diff; + + //Rend_Timer + if (Rend_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_REND); + Rend_Timer = 18000; + }else Rend_Timer -= diff; + + //SunderArmor_Timer + if (SunderArmor_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SUNDERARMOR); + SunderArmor_Timer = 25000; + }else SunderArmor_Timer -= diff; + + //KnockAway_Timer + if (KnockAway_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCKAWAY); + KnockAway_Timer = 12000; + }else KnockAway_Timer -= diff; + + //Slow_Timer + if (Slow_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SLOW); + Slow_Timer = 18000; + }else Slow_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_highlordomokk(Creature *_Creature) +{ + return new boss_highlordomokkAI (_Creature); +} + +void AddSC_boss_highlordomokk() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_highlord_omokk"; + newscript->GetAI = GetAI_boss_highlordomokk; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_mother_smolderweb.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_mother_smolderweb.cpp new file mode 100644 index 00000000000..04cef92dcc1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_mother_smolderweb.cpp @@ -0,0 +1,86 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Mother_Smolderweb +SD%Complete: 100 +SDComment: Uncertain how often mother's milk is casted +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_CRYSTALIZE 16104 +#define SPELL_MOTHERSMILK 16468 +#define SPELL_SUMMON_SPIRE_SPIDERLING 16103 + +struct MANGOS_DLL_DECL boss_mothersmolderwebAI : public ScriptedAI +{ + boss_mothersmolderwebAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Crystalize_Timer; + uint32 MothersMilk_Timer; + + void Reset() + { + Crystalize_Timer = 20000; + MothersMilk_Timer = 10000; + } + + void Aggro(Unit *who) { } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if( m_creature->GetHealth() <= damage ) + m_creature->CastSpell(m_creature,SPELL_SUMMON_SPIRE_SPIDERLING,true); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Crystalize_Timer + if( Crystalize_Timer < diff ) + { + DoCast(m_creature,SPELL_CRYSTALIZE); + Crystalize_Timer = 15000; + }else Crystalize_Timer -= diff; + + //MothersMilk_Timer + if( MothersMilk_Timer < diff ) + { + DoCast(m_creature,SPELL_MOTHERSMILK); + MothersMilk_Timer = 5000+rand()%7500; + }else MothersMilk_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_mothersmolderweb(Creature *_Creature) +{ + return new boss_mothersmolderwebAI (_Creature); +} + +void AddSC_boss_mothersmolderweb() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_mother_smolderweb"; + newscript->GetAI = GetAI_boss_mothersmolderweb; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_overlord_wyrmthalak.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_overlord_wyrmthalak.cpp new file mode 100644 index 00000000000..3eabd522ccc --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_overlord_wyrmthalak.cpp @@ -0,0 +1,127 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Overlord_Wyrmthalak +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_BLASTWAVE 11130 +#define SPELL_SHOUT 23511 +#define SPELL_CLEAVE 20691 +#define SPELL_KNOCKAWAY 20686 + +#define ADD_1X -39.355381 +#define ADD_1Y -513.456482 +#define ADD_1Z 88.472046 +#define ADD_1O 4.679872 + +#define ADD_2X -49.875881 +#define ADD_2Y -511.896942 +#define ADD_2Z 88.195160 +#define ADD_2O 4.613114 + +struct MANGOS_DLL_DECL boss_overlordwyrmthalakAI : public ScriptedAI +{ + boss_overlordwyrmthalakAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 BlastWave_Timer; + uint32 Shout_Timer; + uint32 Cleave_Timer; + uint32 Knockaway_Timer; + bool Summoned; + Creature *SummonedCreature; + + void Reset() + { + BlastWave_Timer = 20000; + Shout_Timer = 2000; + Cleave_Timer = 6000; + Knockaway_Timer = 12000; + Summoned = false; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //BlastWave_Timer + if (BlastWave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_BLASTWAVE); + BlastWave_Timer = 20000; + }else BlastWave_Timer -= diff; + + //Shout_Timer + if (Shout_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHOUT); + Shout_Timer = 10000; + }else Shout_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 7000; + }else Cleave_Timer -= diff; + + //Knockaway_Timer + if (Knockaway_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCKAWAY); + Knockaway_Timer = 14000; + }else Knockaway_Timer -= diff; + + //Summon two Beserks + if ( !Summoned && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 51 ) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + SummonedCreature = m_creature->SummonCreature(9216,ADD_1X,ADD_1Y,ADD_1Z,ADD_1O,TEMPSUMMON_TIMED_DESPAWN,300000); + ((CreatureAI*)SummonedCreature->AI())->AttackStart(target); + SummonedCreature = m_creature->SummonCreature(9268,ADD_2X,ADD_2Y,ADD_2Z,ADD_2O,TEMPSUMMON_TIMED_DESPAWN,300000); + ((CreatureAI*)SummonedCreature->AI())->AttackStart(target); + Summoned = true; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_overlordwyrmthalak(Creature *_Creature) +{ + return new boss_overlordwyrmthalakAI (_Creature); +} + +void AddSC_boss_overlordwyrmthalak() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_overlord_wyrmthalak"; + newscript->GetAI = GetAI_boss_overlordwyrmthalak; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_pyroguard_emberseer.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_pyroguard_emberseer.cpp new file mode 100644 index 00000000000..248ea48f9e0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_pyroguard_emberseer.cpp @@ -0,0 +1,93 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Pyroguard_Emberseer +SD%Complete: 100 +SDComment: Event to activate Emberseer NYI +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FIRENOVA 23462 +#define SPELL_FLAMEBUFFET 23341 +#define SPELL_PYROBLAST 17274 + +struct MANGOS_DLL_DECL boss_pyroguard_emberseerAI : public ScriptedAI +{ + boss_pyroguard_emberseerAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FireNova_Timer; + uint32 FlameBuffet_Timer; + uint32 PyroBlast_Timer; + + void Reset() + { + FireNova_Timer = 6000; + FlameBuffet_Timer = 3000; + PyroBlast_Timer = 14000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //FireNova_Timer + if (FireNova_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIRENOVA); + FireNova_Timer = 6000; + }else FireNova_Timer -= diff; + + //FlameBuffet_Timer + if (FlameBuffet_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FLAMEBUFFET); + FlameBuffet_Timer = 14000; + }else FlameBuffet_Timer -= diff; + + //PyroBlast_Timer + if (PyroBlast_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_PYROBLAST); + PyroBlast_Timer = 15000; + }else PyroBlast_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_pyroguard_emberseer(Creature *_Creature) +{ + return new boss_pyroguard_emberseerAI (_Creature); +} + +void AddSC_boss_pyroguard_emberseer() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_pyroguard_emberseer"; + newscript->GetAI = GetAI_boss_pyroguard_emberseer; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_quartermaster_zigris.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_quartermaster_zigris.cpp new file mode 100644 index 00000000000..6955ae8ad71 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_quartermaster_zigris.cpp @@ -0,0 +1,85 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Quartmaster_Zigris +SD%Complete: 100 +SDComment: Needs revision +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHOOT 16496 +#define SPELL_STUNBOMB 16497 +#define SPELL_HEALING_POTION 15504 +#define SPELL_HOOKEDNET 15609 + +struct MANGOS_DLL_DECL boss_quatermasterzigrisAI : public ScriptedAI +{ + boss_quatermasterzigrisAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Shoot_Timer; + uint32 StunBomb_Timer; + //uint32 HelingPotion_Timer; + + void Reset() + { + Shoot_Timer = 1000; + StunBomb_Timer = 16000; + //HelingPotion_Timer = 25000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Shoot_Timer + if (Shoot_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHOOT); + Shoot_Timer = 500; + }else Shoot_Timer -= diff; + + //StunBomb_Timer + if (StunBomb_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_STUNBOMB); + StunBomb_Timer = 14000; + }else StunBomb_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_quatermasterzigris(Creature *_Creature) +{ + return new boss_quatermasterzigrisAI (_Creature); +} + +void AddSC_boss_quatermasterzigris() +{ + Script *newscript; + newscript = new Script; + newscript->Name="quartermaster_zigris"; + newscript->GetAI = GetAI_boss_quatermasterzigris; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_rend_blackhand.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_rend_blackhand.cpp new file mode 100644 index 00000000000..679f92a06b6 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_rend_blackhand.cpp @@ -0,0 +1,91 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Rend_Blackhand +SD%Complete: 100 +SDComment: Intro event NYI +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_WHIRLWIND 26038 +#define SPELL_CLEAVE 20691 +#define SPELL_THUNDERCLAP 23931 //Not sure if he cast this spell + +struct MANGOS_DLL_DECL boss_rend_blackhandAI : public ScriptedAI +{ + boss_rend_blackhandAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 WhirlWind_Timer; + uint32 Cleave_Timer; + uint32 Thunderclap_Timer; + + void Reset() + { + WhirlWind_Timer = 20000; + Cleave_Timer = 5000; + Thunderclap_Timer = 9000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //WhirlWind_Timer + if (WhirlWind_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_WHIRLWIND); + WhirlWind_Timer = 18000; + }else WhirlWind_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 10000; + }else Cleave_Timer -= diff; + + //Thunderclap_Timer + if (Thunderclap_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_THUNDERCLAP); + Thunderclap_Timer = 16000; + }else Thunderclap_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_rend_blackhand(Creature *_Creature) +{ + return new boss_rend_blackhandAI (_Creature); +} + +void AddSC_boss_rend_blackhand() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_rend_blackhand"; + newscript->GetAI = GetAI_boss_rend_blackhand; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_shadow_hunter_voshgajin.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_shadow_hunter_voshgajin.cpp new file mode 100644 index 00000000000..bc43f2e223a --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_shadow_hunter_voshgajin.cpp @@ -0,0 +1,95 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Shadow_Hunter_Voshgajin +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_CURSEOFBLOOD 24673 +#define SPELL_HEX 16708 +#define SPELL_CLEAVE 20691 + +struct MANGOS_DLL_DECL boss_shadowvoshAI : public ScriptedAI +{ + boss_shadowvoshAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 CurseOfBlood_Timer; + uint32 Hex_Timer; + uint32 Cleave_Timer; + + void Reset() + { + CurseOfBlood_Timer = 2000; + Hex_Timer = 8000; + Cleave_Timer = 14000; + + //m_creature->CastSpell(m_creature,SPELL_ICEARMOR,true); + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //CurseOfBlood_Timer + if (CurseOfBlood_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CURSEOFBLOOD); + CurseOfBlood_Timer = 45000; + }else CurseOfBlood_Timer -= diff; + + //Hex_Timer + if (Hex_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_HEX); + Hex_Timer = 15000; + }else Hex_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 7000; + }else Cleave_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_shadowvosh(Creature *_Creature) +{ + return new boss_shadowvoshAI (_Creature); +} + +void AddSC_boss_shadowvosh() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_shadow_hunter_voshgajin"; + newscript->GetAI = GetAI_boss_shadowvosh; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_the_beast.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_the_beast.cpp new file mode 100644 index 00000000000..93a30ecd204 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_the_beast.cpp @@ -0,0 +1,93 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_The_Best +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FLAMEBREAK 16785 +#define SPELL_IMMOLATE 20294 +#define SPELL_TERRIFYINGROAR 14100 + +struct MANGOS_DLL_DECL boss_thebeastAI : public ScriptedAI +{ + boss_thebeastAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Flamebreak_Timer; + uint32 Immolate_Timer; + uint32 TerrifyingRoar_Timer; + + void Reset() + { + Flamebreak_Timer = 12000; + Immolate_Timer = 3000; + TerrifyingRoar_Timer = 23000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Flamebreak_Timer + if (Flamebreak_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FLAMEBREAK); + Flamebreak_Timer = 10000; + }else Flamebreak_Timer -= diff; + + //Immolate_Timer + if (Immolate_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_IMMOLATE); + Immolate_Timer = 8000; + }else Immolate_Timer -= diff; + + //TerrifyingRoar_Timer + if (TerrifyingRoar_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_TERRIFYINGROAR); + TerrifyingRoar_Timer = 20000; + }else TerrifyingRoar_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_thebeast(Creature *_Creature) +{ + return new boss_thebeastAI (_Creature); +} + +void AddSC_boss_thebeast() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_the_beast"; + newscript->GetAI = GetAI_boss_thebeast; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackrock_spire/boss_warmaster_voone.cpp b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_warmaster_voone.cpp new file mode 100644 index 00000000000..6621c4672a1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackrock_spire/boss_warmaster_voone.cpp @@ -0,0 +1,121 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Warmaster_Voone +SD%Complete: 100 +SDComment: +SDCategory: Blackrock Spire +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SNAPKICK 15618 +#define SPELL_CLEAVE 15579 +#define SPELL_UPPERCUT 10966 +#define SPELL_MORTALSTRIKE 16856 +#define SPELL_PUMMEL 15615 +#define SPELL_THROWAXE 16075 + +struct MANGOS_DLL_DECL boss_warmastervooneAI : public ScriptedAI +{ + boss_warmastervooneAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Snapkick_Timer; + uint32 Cleave_Timer; + uint32 Uppercut_Timer; + uint32 MortalStrike_Timer; + uint32 Pummel_Timer; + uint32 ThrowAxe_Timer; + + void Reset() + { + Snapkick_Timer = 8000; + Cleave_Timer = 14000; + Uppercut_Timer = 20000; + MortalStrike_Timer = 12000; + Pummel_Timer = 32000; + ThrowAxe_Timer = 1000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Snapkick_Timer + if (Snapkick_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SNAPKICK); + Snapkick_Timer = 6000; + }else Snapkick_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 12000; + }else Cleave_Timer -= diff; + + //Uppercut_Timer + if (Uppercut_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_UPPERCUT); + Uppercut_Timer = 14000; + }else Uppercut_Timer -= diff; + + //MortalStrike_Timer + if (MortalStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MORTALSTRIKE); + MortalStrike_Timer = 10000; + }else MortalStrike_Timer -= diff; + + //Pummel_Timer + if (Pummel_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_PUMMEL); + Pummel_Timer = 16000; + }else Pummel_Timer -= diff; + + //ThrowAxe_Timer + if (ThrowAxe_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_THROWAXE); + ThrowAxe_Timer = 8000; + }else ThrowAxe_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_warmastervoone(Creature *_Creature) +{ + return new boss_warmastervooneAI (_Creature); +} + +void AddSC_boss_warmastervoone() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_warmaster_voone"; + newscript->GetAI = GetAI_boss_warmastervoone; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackwing_lair/boss_broodlord_lashlayer.cpp b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_broodlord_lashlayer.cpp new file mode 100644 index 00000000000..a1f3260550b --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_broodlord_lashlayer.cpp @@ -0,0 +1,130 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Broodlord_Lashlayer +SD%Complete: 100 +SDComment: +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_CLEAVE 26350 +#define SPELL_BLASTWAVE 23331 +#define SPELL_MORTALSTRIKE 24573 +#define SPELL_KNOCKBACK 25778 + +#define SAY_AGGRO "None of your kind should be here! You've doomed only yourselves!" +#define SAY_LEASH "Clever Mortals but I am not so easily lured away from my sanctum!" +#define SOUND_AGGRO 8286 +#define SOUND_LEASH 8287 + +struct MANGOS_DLL_DECL boss_broodlordAI : public ScriptedAI +{ + boss_broodlordAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Cleave_Timer; + uint32 BlastWave_Timer; + uint32 MortalStrike_Timer; + uint32 KnockBack_Timer; + uint32 LeashCheck_Timer; + + void Reset() + { + Cleave_Timer = 8000; //These times are probably wrong + BlastWave_Timer = 12000; + MortalStrike_Timer = 20000; + KnockBack_Timer = 30000; + LeashCheck_Timer = 2000; + + m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); + m_creature->ApplySpellImmune(1, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //LeashCheck_Timer + if (LeashCheck_Timer < diff) + { + float rx,ry,rz; + m_creature->GetRespawnCoord(rx, ry, rz); + float spawndist = m_creature->GetDistance(rx,ry,rz); + if ( spawndist > 250 ) + { + EnterEvadeMode(); + return; + } + LeashCheck_Timer = 2000; + }else LeashCheck_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 7000; + }else Cleave_Timer -= diff; + + // BlastWave + if (BlastWave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_BLASTWAVE); + BlastWave_Timer = 8000 + rand()%8000; + }else BlastWave_Timer -= diff; + + //MortalStrike_Timer + if (MortalStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MORTALSTRIKE); + MortalStrike_Timer = 25000 + rand()%10000; + }else MortalStrike_Timer -= diff; + + if (KnockBack_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCKBACK); + //Drop 50% aggro + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-50); + + KnockBack_Timer = 15000 + rand()%15000; + }else KnockBack_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_broodlord(Creature *_Creature) +{ + return new boss_broodlordAI (_Creature); +} + +void AddSC_boss_broodlord() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_broodlord"; + newscript->GetAI = GetAI_boss_broodlord; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackwing_lair/boss_chromaggus.cpp b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_chromaggus.cpp new file mode 100644 index 00000000000..db05dab31b6 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_chromaggus.cpp @@ -0,0 +1,320 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Chromaggus +SD%Complete: 95 +SDComment: Chromatic Mutation disabled due to lack of core support +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" + +//These spells are actually called elemental shield +//What they do is decrease all damage by 75% then they increase +//One school of damage by 1100% +#define SPELL_FIRE_VURNALBILTY 22277 +#define SPELL_FROST_VURNALBILTY 22278 +#define SPELL_SHADOW_VURNALBILTY 22279 +#define SPELL_NATURE_VURNALBILTY 22280 +#define SPELL_ARCANE_VURNALBILTY 22281 + +#define EMOTE_FRENZY "goes into a killing frenzy!" +#define EMOTE_SHIMMER "flinches as its skin shimmers" + +#define SPELL_INCINERATE 23308 //Incinerate 23308,23309 +#define SPELL_TIMELAPSE 23310 //Time lapse 23310, 23311(old threat mod that was removed in 2.01) +#define SPELL_CORROSIVEACID 23313 //Corrosive Acid 23313, 23314 +#define SPELL_IGNITEFLESH 23315 //Ignite Flesh 23315,23316 +#define SPELL_FROSTBURN 23187 //Frost burn 23187, 23189 + +//Brood Affliction 23173 - Scripted Spell that cycles through all targets within 100 yards and has a chance to cast one of the afflictions on them +//Since Scripted spells arn't coded I'll just write a function that does the same thing + +#define SPELL_BROODAF_BLUE 23153 //Blue affliction 23153 +#define SPELL_BROODAF_BLACK 23154 //Black affliction 23154 +#define SPELL_BROODAF_RED 23155 //Red affliction 23155 (23168 on death) +#define SPELL_BROODAF_BRONZE 23170 //Bronze Affliction 23170 +#define SPELL_BROODAF_GREEN 23169 //Brood Affliction Green 23169 + +#define SPELL_CHROMATIC_MUT_1 23174 //Spell cast on player if they get all 5 debuffs + +#define SPELL_FRENZY 28371 //The frenzy spell may be wrong +#define SPELL_ENRAGE 28747 + +#define TEMP_MUTATE_WHISPER "[SD2 Debug] You would be mind controlled here!" + +struct MANGOS_DLL_DECL boss_chromaggusAI : public ScriptedAI +{ + boss_chromaggusAI(Creature *c) : ScriptedAI(c) + { + //Select the 2 breaths that we are going to use until despawned + //5 possiblities for the first breath, 4 for the second, 20 total possiblites + //This way we don't end up casting 2 of the same breath + //TL TL would be stupid + srand(time(NULL)); + switch (rand()%20) + { + //B1 - Incin + case 0: + Breath1_Spell = SPELL_INCINERATE; + Breath2_Spell = SPELL_TIMELAPSE; + break; + case 1: + Breath1_Spell = SPELL_INCINERATE; + Breath2_Spell = SPELL_CORROSIVEACID; + break; + case 2: + Breath1_Spell = SPELL_INCINERATE; + Breath2_Spell = SPELL_IGNITEFLESH; + break; + case 3: + Breath1_Spell = SPELL_INCINERATE; + Breath2_Spell = SPELL_FROSTBURN; + break; + + //B1 - TL + case 4: + Breath1_Spell = SPELL_TIMELAPSE; + Breath2_Spell = SPELL_INCINERATE; + break; + case 5: + Breath1_Spell = SPELL_TIMELAPSE; + Breath2_Spell = SPELL_CORROSIVEACID; + break; + case 6: + Breath1_Spell = SPELL_TIMELAPSE; + Breath2_Spell = SPELL_IGNITEFLESH; + break; + case 7: + Breath1_Spell = SPELL_TIMELAPSE; + Breath2_Spell = SPELL_FROSTBURN; + break; + + //B1 - Acid + case 8: + Breath1_Spell = SPELL_CORROSIVEACID; + Breath2_Spell = SPELL_INCINERATE; + break; + case 9: + Breath1_Spell = SPELL_CORROSIVEACID; + Breath2_Spell = SPELL_TIMELAPSE; + break; + case 10: + Breath1_Spell = SPELL_CORROSIVEACID; + Breath2_Spell = SPELL_IGNITEFLESH; + break; + case 11: + Breath1_Spell = SPELL_CORROSIVEACID; + Breath2_Spell = SPELL_FROSTBURN; + break; + + //B1 - Ignite + case 12: + Breath1_Spell = SPELL_IGNITEFLESH; + Breath2_Spell = SPELL_INCINERATE; + break; + case 13: + Breath1_Spell = SPELL_IGNITEFLESH; + Breath2_Spell = SPELL_CORROSIVEACID; + break; + case 14: + Breath1_Spell = SPELL_IGNITEFLESH; + Breath2_Spell = SPELL_TIMELAPSE; + break; + case 15: + Breath1_Spell = SPELL_IGNITEFLESH; + Breath2_Spell = SPELL_FROSTBURN; + break; + + //B1 - Frost + case 16: + Breath1_Spell = SPELL_FROSTBURN; + Breath2_Spell = SPELL_INCINERATE; + break; + case 17: + Breath1_Spell = SPELL_FROSTBURN; + Breath2_Spell = SPELL_TIMELAPSE; + break; + case 18: + Breath1_Spell = SPELL_FROSTBURN; + Breath2_Spell = SPELL_CORROSIVEACID; + break; + case 19: + Breath1_Spell = SPELL_FROSTBURN; + Breath2_Spell = SPELL_IGNITEFLESH; + break; + }; + + EnterEvadeMode(); + } + + uint32 Breath1_Spell; + uint32 Breath2_Spell; + uint32 CurrentVurln_Spell; + + uint32 Shimmer_Timer; + uint32 Breath1_Timer; + uint32 Breath2_Timer; + uint32 Affliction_Timer; + uint32 Frenzy_Timer; + bool Enraged; + + void Reset() + { + CurrentVurln_Spell = 0; //We use this to store our last vurlnability spell so we can remove it later + + Shimmer_Timer = 0; //Time till we change vurlnerabilites + Breath1_Timer = 30000; //First breath is 30 seconds + Breath2_Timer = 60000; //Second is 1 minute so that we can alternate + Affliction_Timer = 10000; //This is special - 5 seconds means that we cast this on 1 player every 5 sconds + Frenzy_Timer = 15000; + + Enraged = false; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Shimmer_Timer Timer + if (Shimmer_Timer < diff) + { + //Remove old vurlnability spell + if (CurrentVurln_Spell) + m_creature->RemoveAurasDueToSpell(CurrentVurln_Spell); + + //Cast new random vurlnabilty on self + uint32 spell; + switch (rand()%5) + { + case 0: spell = SPELL_FIRE_VURNALBILTY; break; + case 1: spell = SPELL_FROST_VURNALBILTY; break; + case 2: spell = SPELL_SHADOW_VURNALBILTY; break; + case 3: spell = SPELL_NATURE_VURNALBILTY; break; + case 4: spell = SPELL_ARCANE_VURNALBILTY; break; + } + + DoCast(m_creature,spell); + CurrentVurln_Spell = spell; + + DoTextEmote(EMOTE_SHIMMER, NULL); + Shimmer_Timer = 45000; + }else Shimmer_Timer -= diff; + + //Breath1_Timer + if (Breath1_Timer < diff) + { + DoCast(m_creature->getVictim(),Breath1_Spell); + Breath1_Timer = 60000; + }else Breath1_Timer -= diff; + + //Breath2_Timer + if (Breath2_Timer < diff) + { + DoCast(m_creature->getVictim(),Breath2_Spell); + Breath2_Timer = 60000; + }else Breath2_Timer -= diff; + + //Affliction_Timer + if (Affliction_Timer < diff) + { + uint32 SpellAfflict = 0; + + switch (rand()%5) + { + case 0: SpellAfflict = SPELL_BROODAF_BLUE; break; + case 1: SpellAfflict = SPELL_BROODAF_BLACK; break; + case 2: SpellAfflict = SPELL_BROODAF_RED; break; + case 3: SpellAfflict = SPELL_BROODAF_BRONZE; break; + case 4: SpellAfflict = SPELL_BROODAF_GREEN; break; + } + + std::list::iterator i; + + for (i = m_creature->getThreatManager().getThreatList().begin();i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + Unit* pUnit = NULL; + pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + + if(pUnit) + { + //Cast affliction + DoCast(pUnit, SpellAfflict, true); + + //Chromatic mutation if target is effected by all afflictions + if (pUnit->HasAura(SPELL_BROODAF_BLUE,0) + && pUnit->HasAura(SPELL_BROODAF_BLACK,0) + && pUnit->HasAura(SPELL_BROODAF_RED,0) + && pUnit->HasAura(SPELL_BROODAF_BRONZE,0) + && pUnit->HasAura(SPELL_BROODAF_GREEN,0)) + { + //target->RemoveAllAuras(); + //DoCast(target,SPELL_CHROMATIC_MUT_1); + + //Chromatic mutation is causing issues + //Assuming it is caused by a lack of core support for Charm + //So instead we instant kill our target + + //WORKAROUND + if (pUnit->GetTypeId() == TYPEID_PLAYER) + { + DoWhisper(TEMP_MUTATE_WHISPER, pUnit); + pUnit->CastSpell(pUnit, 5, false); + } + } + } + } + + Affliction_Timer = 10000; + }else Affliction_Timer -= diff; + + //Frenzy_Timer + if (Frenzy_Timer < diff) + { + DoCast(m_creature,SPELL_FRENZY); + DoTextEmote(EMOTE_FRENZY,NULL); + Frenzy_Timer = 10000 + (rand() % 5000); + }else Frenzy_Timer -= diff; + + //Enrage if not already enraged and below 20% + if (!Enraged && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20) + { + DoCast(m_creature,SPELL_ENRAGE); + Enraged = true; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_chromaggus(Creature *_Creature) +{ + return new boss_chromaggusAI (_Creature); +} + +void AddSC_boss_chromaggus() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_chromaggus"; + newscript->GetAI = GetAI_boss_chromaggus; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackwing_lair/boss_ebonroc.cpp b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_ebonroc.cpp new file mode 100644 index 00000000000..91d57238f01 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_ebonroc.cpp @@ -0,0 +1,103 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Ebonroc +SD%Complete: 50 +SDComment: Shadow of Ebonroc needs core support +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWFLAME 22539 +#define SPELL_WINGBUFFET 18500 +#define SPELL_SHADOWOFEBONROC 23340 +#define SPELL_HEAL 41386 //Thea Heal spell of his Shadow + +struct MANGOS_DLL_DECL boss_ebonrocAI : public ScriptedAI +{ + boss_ebonrocAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowFlame_Timer; + uint32 WingBuffet_Timer; + uint32 ShadowOfEbonroc_Timer; + uint32 Heal_Timer; + + void Reset() + { + ShadowFlame_Timer = 15000; //These times are probably wrong + WingBuffet_Timer = 30000; + ShadowOfEbonroc_Timer = 45000; + Heal_Timer = 1000; + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Shadowflame Timer + if (ShadowFlame_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWFLAME); + ShadowFlame_Timer = 12000 + rand()%3000; + }else ShadowFlame_Timer -= diff; + + //Wing Buffet Timer + if (WingBuffet_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_WINGBUFFET); + WingBuffet_Timer = 25000; + }else WingBuffet_Timer -= diff; + + //Shadow of Ebonroc Timer + if (ShadowOfEbonroc_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWOFEBONROC); + ShadowOfEbonroc_Timer = 25000 + rand()%10000; + }else ShadowOfEbonroc_Timer -= diff; + + if (m_creature->getVictim()->HasAura(SPELL_SHADOWOFEBONROC,0)) + { + if (Heal_Timer < diff) + { + DoCast(m_creature, SPELL_HEAL); + Heal_Timer = 1000 + rand()%2000; + }else Heal_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_ebonroc(Creature *_Creature) +{ + return new boss_ebonrocAI (_Creature); +} + +void AddSC_boss_ebonroc() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_ebonroc"; + newscript->GetAI = GetAI_boss_ebonroc; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackwing_lair/boss_firemaw.cpp b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_firemaw.cpp new file mode 100644 index 00000000000..f4b7848a3bb --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_firemaw.cpp @@ -0,0 +1,94 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Firemaw +SD%Complete: 100 +SDComment: +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWFLAME 22539 +#define SPELL_WINGBUFFET 23339 +#define SPELL_FLAMEBUFFET 23341 + +struct MANGOS_DLL_DECL boss_firemawAI : public ScriptedAI +{ + boss_firemawAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowFlame_Timer; + uint32 WingBuffet_Timer; + uint32 FlameBuffet_Timer; + + void Reset() + { + ShadowFlame_Timer = 30000; //These times are probably wrong + WingBuffet_Timer = 24000; + FlameBuffet_Timer = 5000; + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ShadowFlame_Timer + if (ShadowFlame_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWFLAME); + ShadowFlame_Timer = 15000 + rand()%3000; + }else ShadowFlame_Timer -= diff; + + //WingBuffet_Timer + if (WingBuffet_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_WINGBUFFET); + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-75); + + WingBuffet_Timer = 25000; + }else WingBuffet_Timer -= diff; + + //FlameBuffet_Timer + if (FlameBuffet_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FLAMEBUFFET); + FlameBuffet_Timer = 5000; + }else FlameBuffet_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_firemaw(Creature *_Creature) +{ + return new boss_firemawAI (_Creature); +} + +void AddSC_boss_firemaw() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_firemaw"; + newscript->GetAI = GetAI_boss_firemaw; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackwing_lair/boss_flamegor.cpp b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_flamegor.cpp new file mode 100644 index 00000000000..5bc6531c45a --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_flamegor.cpp @@ -0,0 +1,94 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Flamegor +SD%Complete: 100 +SDComment: +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWFLAME 22539 +#define SPELL_WINGBUFFET 23339 +#define SPELL_FRENZY 23342 //This spell periodically triggers fire nova + +struct MANGOS_DLL_DECL boss_flamegorAI : public ScriptedAI +{ + boss_flamegorAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowFlame_Timer; + uint32 WingBuffet_Timer; + uint32 Frenzy_Timer; + + void Reset() + { + ShadowFlame_Timer = 21000; //These times are probably wrong + WingBuffet_Timer = 35000; + Frenzy_Timer = 10000; + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ShadowFlame_Timer + if (ShadowFlame_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWFLAME); + ShadowFlame_Timer = 15000 + rand()%7000; + }else ShadowFlame_Timer -= diff; + + //WingBuffet_Timer + if (WingBuffet_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_WINGBUFFET); + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-75); + + WingBuffet_Timer = 25000; + }else WingBuffet_Timer -= diff; + + //Frenzy_Timer + if (Frenzy_Timer < diff) + { + DoCast(m_creature,SPELL_FRENZY); + Frenzy_Timer = 8000 + (rand()%2000); + }else Frenzy_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_flamegor(Creature *_Creature) +{ + return new boss_flamegorAI (_Creature); +} + +void AddSC_boss_flamegor() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_flamegor"; + newscript->GetAI = GetAI_boss_flamegor; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackwing_lair/boss_nefarian.cpp b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_nefarian.cpp new file mode 100644 index 00000000000..31495b35d9d --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_nefarian.cpp @@ -0,0 +1,250 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Nefarian +SD%Complete: 100 +SDComment: Some issues with class calls effecting more than one class +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" + +#define SAY_AGGRO "Well done, my minions. The mortals' courage begins to wane! Now, let's see how they contend with the true Lord of Blackrock Spire!" +#define SAY_DEATH "This cannot be! I am the Master here! You mortals are nothing to my kind! DO YOU HEAR? NOTHING!" +#define SAY_RAISE_SKELETONS "Impossible! Rise my minions! Serve your master once more!" +#define SAY_SHADOWFLAME "Burn, you wretches! Burn!" +#define SAY_SLAY "Worthless $N! Your friends will join you soon enough!" +#define SAY_XHEALTH "Enough! Now you vermin shall feel the force of my birthright, the fury of the earth itself." +#define SAY_GAMESBEGIN "Let the games begin!" +#define SAY_START "" + +#define SOUND_AGGRO 8288 +#define SOUND_DEATH 8292 +#define SOUND_RAISE_SKELETONS 8291 +#define SOUND_SHADOWFLAME 8290 +#define SOUND_SLAY 8293 +#define SOUND_XHEALTH 8289 +#define SOUND_GAMESBEGIN 8279 +#define SOUND_START 8280 + +#define SPELL_SHADOWFLAME_INITIAL 22972 +#define SPELL_SHADOWFLAME 22539 +#define SPELL_BELLOWINGROAR 22686 +#define SPELL_VEILOFSHADOW 7068 +#define SPELL_CLEAVE 20691 +#define SPELL_TAILLASH 23364 +#define SPELL_BONECONTRUST 23363 //23362, 23361 + +#define SPELL_MAGE 23410 //wild magic +#define SPELL_WARRIOR 23397 //beserk +#define SPELL_DRUID 23398 // cat form +#define SPELL_PRIEST 23401 // corrupted healing +#define SPELL_PALADIN 23418 //syphon blessing +#define SPELL_SHAMAN 23425 //totems +#define SPELL_WARLOCK 23427 //infernals +#define SPELL_HUNTER 23436 //bow broke +#define SPELL_ROGUE 23414 //Paralise + +#define SAY_MAGE "Mages too? You should be more careful when you play with magic..." +#define SAY_WARRIOR "Warriors, I know you can hit harder than that! Let's see it!" +#define SAY_DRUID "Druids and your silly shapeshifting. Let's see it in action!" +#define SAY_PRIEST "Priests! If you're going to keep healing like that, we might as well make it a little more interesting!" +#define SAY_PALADIN "Paladins, I've heard you have many lives. Show me." +#define SAY_SHAMAN "Shamans, show me what your totems can do!" +#define SAY_WARLOCK "Warlocks, you shouldn't be playing with magic you don't understand. See what happens?" +#define SAY_HUNTER "Hunters and your annoying pea-shooters!" +#define SAY_ROGUE "Rogues? Stop hiding and face me!" + +struct MANGOS_DLL_DECL boss_nefarianAI : public ScriptedAI +{ + boss_nefarianAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowFlame_Timer; + uint32 BellowingRoar_Timer; + uint32 VeilOfShadow_Timer; + uint32 Cleave_Timer; + uint32 TailLash_Timer; + uint32 ClassCall_Timer; + bool Phase3; + + void Reset() + { + ShadowFlame_Timer = 12000; //These times are probably wrong + BellowingRoar_Timer = 30000; + VeilOfShadow_Timer = 15000; + Cleave_Timer = 7000; + TailLash_Timer = 10000; + ClassCall_Timer = 35000; //35-40 seconds + Phase3 = false; + + m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); + m_creature->ApplySpellImmune(1, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true); + } + + void KilledUnit(Unit* Victim) + { + if (rand()%5) + return; + + DoYell(SAY_SLAY,LANG_UNIVERSAL,Victim); + DoPlaySoundToSet(m_creature, SOUND_SLAY); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void Aggro(Unit *who) + { + switch (rand()%3) + { + case 0: + DoYell(SAY_XHEALTH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_XHEALTH); + break; + case 1: + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + break; + case 2: + DoYell(SAY_SHADOWFLAME,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SHADOWFLAME); + break; + } + + DoCast(who,SPELL_SHADOWFLAME_INITIAL); + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ShadowFlame_Timer + if (ShadowFlame_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWFLAME); + ShadowFlame_Timer = 12000; + }else ShadowFlame_Timer -= diff; + + //BellowingRoar_Timer + if (BellowingRoar_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_BELLOWINGROAR); + BellowingRoar_Timer = 30000; + }else BellowingRoar_Timer -= diff; + + //VeilOfShadow_Timer + if (VeilOfShadow_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_VEILOFSHADOW); + VeilOfShadow_Timer = 15000; + }else VeilOfShadow_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 7000; + }else Cleave_Timer -= diff; + + //TailLash_Timer + if (TailLash_Timer < diff) + { + //Cast NYI since we need a better check for behind target + //DoCast(m_creature->getVictim(),SPELL_TAILLASH); + + TailLash_Timer = 10000; + }else TailLash_Timer -= diff; + + //ClassCall_Timer + if (ClassCall_Timer < diff) + { + //Cast a random class call + //On official it is based on what classes are currently on the hostil list + //but we can't do that yet so just randomly call one + + switch (rand()%9) + { + case 0: + DoYell(SAY_MAGE,LANG_UNIVERSAL,NULL); + DoCast(m_creature,SPELL_MAGE); + break; + case 1: + DoYell(SAY_WARRIOR,LANG_UNIVERSAL,NULL); + DoCast(m_creature,SPELL_WARRIOR); + break; + case 2: + DoYell(SAY_DRUID,LANG_UNIVERSAL,NULL); + DoCast(m_creature,SPELL_DRUID); + break; + case 3: + DoYell(SAY_PRIEST,LANG_UNIVERSAL,NULL); + DoCast(m_creature,SPELL_PRIEST); + break; + case 4: + DoYell(SAY_PALADIN,LANG_UNIVERSAL,NULL); + DoCast(m_creature,SPELL_PALADIN); + break; + case 5: + DoYell(SAY_SHAMAN,LANG_UNIVERSAL,NULL); + DoCast(m_creature,SPELL_SHAMAN); + break; + case 6: + DoYell(SAY_WARLOCK,LANG_UNIVERSAL,NULL); + DoCast(m_creature,SPELL_WARLOCK); + break; + case 7: + DoYell(SAY_HUNTER,LANG_UNIVERSAL,NULL); + DoCast(m_creature,SPELL_HUNTER); + break; + case 8: + DoYell(SAY_ROGUE,LANG_UNIVERSAL,NULL); + DoCast(m_creature,SPELL_ROGUE); + break; + } + + ClassCall_Timer = 35000 + (rand() % 5000); + }else ClassCall_Timer -= diff; + + //Phase3 begins when we are below X health + if (!Phase3 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20) + { + Phase3 = true; + DoYell(SAY_RAISE_SKELETONS,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RAISE_SKELETONS); + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_nefarian(Creature *_Creature) +{ + return new boss_nefarianAI (_Creature); +} + +void AddSC_boss_nefarian() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_nefarian"; + newscript->GetAI = GetAI_boss_nefarian; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackwing_lair/boss_razorgore.cpp b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_razorgore.cpp new file mode 100644 index 00000000000..78441b63888 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_razorgore.cpp @@ -0,0 +1,126 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Razorgore +SD%Complete: 50 +SDComment: Needs additional review. Phase 1 NYI +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" + +//Razorgore Phase 2 Script + +#define SAY_NPC_DEATH "If I fall into the abyss I'll take all of you mortals with me..." +#define SOUND_NPC_DEATH 8278 +#define SAY_EGGS_BREAK3 "No! Not another one! I'll have your heads for this atrocity." +#define SOUND_EGGS_BREAK3 8277 + +#define SPELL_CLEAVE 22540 +#define SPELL_WARSTOMP 24375 +#define SPELL_FIREBALLVOLLEY 22425 +#define SPELL_CONFLAGRATION 23023 + +struct MANGOS_DLL_DECL boss_razorgoreAI : public ScriptedAI +{ + boss_razorgoreAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Cleave_Timer; + uint32 WarStomp_Timer; + uint32 FireballVolley_Timer; + uint32 Conflagration_Timer; + + void Reset() + { + Cleave_Timer = 15000; //These times are probably wrong + WarStomp_Timer = 35000; + FireballVolley_Timer = 7000; + Conflagration_Timer = 12000; + + m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); + m_creature->ApplySpellImmune(1, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true); + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 7000 + rand()%3000; + }else Cleave_Timer -= diff; + + //WarStomp_Timer + if (WarStomp_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_WARSTOMP); + WarStomp_Timer = 15000 + rand()%10000; + }else WarStomp_Timer -= diff; + + //FireballVolley_Timer + if (FireballVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIREBALLVOLLEY); + FireballVolley_Timer = 12000 + rand()%3000; + }else FireballVolley_Timer -= diff; + + //Conflagration_Timer + if (Conflagration_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CONFLAGRATION); + //We will remove this threat reduction and add an aura check. + + //if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + //m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-50); + + Conflagration_Timer = 12000; + }else Conflagration_Timer -= diff; + + // Aura Check. If the gamer is affected by confliguration we attack a random gamer. + if (m_creature->getVictim()->HasAura(SPELL_CONFLAGRATION,0)) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,1); + if (target) + m_creature->TauntApply(target); + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_razorgore(Creature *_Creature) +{ + return new boss_razorgoreAI (_Creature); +} + +void AddSC_boss_razorgore() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_razorgore"; + newscript->GetAI = GetAI_boss_razorgore; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackwing_lair/boss_vaelastrasz.cpp b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_vaelastrasz.cpp new file mode 100644 index 00000000000..217eebfb852 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_vaelastrasz.cpp @@ -0,0 +1,278 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Vaelastrasz +SD%Complete: 75 +SDComment: Burning Adrenaline not correctly implemented in core +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_ESSENCEOFTHERED 23513 +#define SPELL_FLAMEBREATH 23461 +#define SPELL_FIRENOVA 23462 +#define SPELL_TAILSWIPE 15847 +#define SPELL_BURNINGADRENALINE 23620 +#define SPELL_CLEAVE 20684 //Chain cleave is most likely named something different and contains a dummy effect + +#define SAY_LINE1 "Too late...friends. Nefarius' corruption has taken hold. I cannot...control myself. " +#define SOUND_LINE1 8281 + +#define SAY_LINE2 "I beg you Mortals, flee! Flee before I lose all control. The Black Fire rages within my heart. I must release it!" +#define SOUND_LINE2 8282 + +#define SAY_LINE3 "FLAME! DEATH! DESTRUCTION! COWER MORTALS BEFORE THE WRATH OF LORD....NO! I MUST FIGHT THIS!" +#define SOUND_LINE3 8283 + +#define SAY_HALFLIFE "Nefarius' hate has made me stronger than ever before. You should have fled, while you could, mortals! The fury of Blackrock courses through my veins! " +#define SOUND_HALFLIFE 8285 + +#define SAY_KILLTARGET "Forgive me $N, your death only adds to my failure." +#define SOUND_KILLTARGET 8284 + +#define GOSSIP_ITEM "Start Event " + +struct MANGOS_DLL_DECL boss_vaelAI : public ScriptedAI +{ + boss_vaelAI(Creature *c) : ScriptedAI(c) + { + c->SetUInt32Value(UNIT_NPC_FLAGS,1); + c->setFaction(35); + c->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Reset(); + } + + uint64 PlayerGUID; + uint32 SpeachTimer; + uint32 SpeachNum; + uint32 Cleave_Timer; + uint32 FlameBreath_Timer; + uint32 FireNova_Timer; + uint32 BurningAdrenalineCaster_Timer; + uint32 BurningAdrenalineTank_Timer; + uint32 TailSwipe_Timer; + bool HasYelled; + bool DoingSpeach; + + void Reset() + { + PlayerGUID = 0; + SpeachTimer = 0; + SpeachNum = 0; + Cleave_Timer = 8000; //These times are probably wrong + FlameBreath_Timer = 11000; + BurningAdrenalineCaster_Timer = 15000; + BurningAdrenalineTank_Timer = 45000; + FireNova_Timer = 5000; + TailSwipe_Timer = 20000; + HasYelled = false; + DoingSpeach = false; + + m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); + m_creature->ApplySpellImmune(1, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true); + } + + void BeginSpeach(Unit* target) + { + //Stand up and begin speach + PlayerGUID = target->GetGUID(); + + //10 seconds + DoYell(SAY_LINE1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_LINE1); + SpeachTimer = 10000; + SpeachNum = 0; + DoingSpeach = true; + } + + void KilledUnit(Unit *victim) + { + if (rand()%5) + return; + + DoYell(SAY_KILLTARGET,LANG_UNIVERSAL,victim); + DoPlaySoundToSet(m_creature,SOUND_KILLTARGET); + } + + void Aggro(Unit *who) + { + DoCast(m_creature,SPELL_ESSENCEOFTHERED); + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + //Speach + if (DoingSpeach) + if (SpeachTimer < diff) + { + switch (SpeachNum) + { + case 0: + //16 seconds till next line + DoYell(SAY_LINE2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_LINE2); + SpeachTimer = 16000; + SpeachNum++; + break; + + case 1: + //This one is actually 16 seconds but we only go to 10 seconds because he starts attacking after he says "I must fight this!" + DoYell(SAY_LINE3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_LINE3); + SpeachTimer = 10000; + SpeachNum++; + break; + + case 2: + default: + m_creature->setFaction(103); + m_creature->SetHealth(int(m_creature->GetMaxHealth()*.3)); + if (PlayerGUID && Unit::GetUnit((*m_creature),PlayerGUID)) + { + DoStartAttackAndMovement(Unit::GetUnit((*m_creature),PlayerGUID)); + DoCast(m_creature,SPELL_ESSENCEOFTHERED); + } + + SpeachTimer = 0; + DoingSpeach = false; + break; + } + }else SpeachTimer -= diff; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + // Yell if hp lower than 15% + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 15 && !HasYelled) + { + //Say our dialog + DoYell(SAY_HALFLIFE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_HALFLIFE); + HasYelled = true; + } + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 15000; + }else Cleave_Timer -= diff; + + //FlameBreath_Timer + if (FlameBreath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FLAMEBREATH); + FlameBreath_Timer = 4000 + rand()%4000; + }else FlameBreath_Timer -= diff; + + //BurningAdrenalineCaster_Timer + if (BurningAdrenalineCaster_Timer < diff) + { + Unit* target = NULL; + + int i = 0 ; + while (i < 3) // max 3 tries to get a random target with power_mana + { + ++i; + target = SelectUnit(SELECT_TARGET_RANDOM,1);//not aggro leader + if (target) + if (target->getPowerType() == POWER_MANA) + i=3; + } + if (target) // cast on self (see below) + target->CastSpell(target,SPELL_BURNINGADRENALINE,1); + + BurningAdrenalineCaster_Timer = 15000; + }else BurningAdrenalineCaster_Timer -= diff; + + //BurningAdrenalineTank_Timer + if (BurningAdrenalineTank_Timer < diff) + { + // have the victim cast the spell on himself otherwise the third effect aura will be applied + // to Vael instead of the player + + m_creature->getVictim()->CastSpell(m_creature->getVictim(),SPELL_BURNINGADRENALINE,1); + + BurningAdrenalineTank_Timer = 45000; + }else BurningAdrenalineTank_Timer -= diff; + + //FireNova_Timer + if (FireNova_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIRENOVA); + FireNova_Timer = 5000; + }else FireNova_Timer -= diff; + + //TailSwipe_Timer + if (TailSwipe_Timer < diff) + { + //Only cast if we are behind + /*if (!m_creature->HasInArc( M_PI, m_creature->getVictim())) + { + DoCast(m_creature->getVictim(),SPELL_TAILSWIPE); + }*/ + + TailSwipe_Timer = 20000; + }else TailSwipe_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +void SendDefaultMenu_boss_vael(Player *player, Creature *_Creature, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) //Fight time + { + player->CLOSE_GOSSIP_MENU(); + ((boss_vaelAI*)_Creature->AI())->BeginSpeach((Unit*)player); + } +} + +bool GossipSelect_boss_vael(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (sender == GOSSIP_SENDER_MAIN) + SendDefaultMenu_boss_vael(player, _Creature, action); + + return true; +} + +bool GossipHello_boss_vael(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(907,_Creature->GetGUID()); + + return true; +} + +CreatureAI* GetAI_boss_vael(Creature *_Creature) +{ + return new boss_vaelAI (_Creature); +} + +void AddSC_boss_vael() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_vaelastrasz"; + newscript->GetAI = GetAI_boss_vael; + newscript->pGossipHello = &GossipHello_boss_vael; + newscript->pGossipSelect = &GossipSelect_boss_vael; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackwing_lair/boss_victor_nefarius.cpp b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_victor_nefarius.cpp new file mode 100644 index 00000000000..3870a247fa0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackwing_lair/boss_victor_nefarius.cpp @@ -0,0 +1,399 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Victor_Nefarius +SD%Complete: 75 +SDComment: Missing some text, Vael beginning event, and spawns Nef in wrong place +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" + +#define SAY_GAMESBEGIN_1 "In this world where time is your enemy, it is my greatest ally. This grand game of life that you think you play in fact plays you. To that I say..." +#define SAY_GAMESBEGIN_2 "Let the games begin!" +#define SAY_VAEL_INTRO "" + +#define SOUND_GAMESBEGIN 8280 +#define SOUND_VAEL_INTRO 8279 + +#define GOSSIP_ITEM_1 "I've made no mistakes." +#define GOSSIP_ITEM_2 "You have lost your mind, Nefarius. You speak in riddles." +#define GOSSIP_ITEM_3 "Please do." + +#define CREATURE_BRONZE_DRAKANOID 14263 +#define CREATURE_BLUE_DRAKANOID 14261 +#define CREATURE_RED_DRAKANOID 14264 +#define CREATURE_GREEN_DRAKANOID 14262 +#define CREATURE_BLACK_DRAKANOID 14265 + +#define CREATURE_CHROMATIC_DRAKANOID 14302 + +#define CREATURE_NEFARIAN 11583 + +#define ADD_X1 -7591.151855 +#define ADD_X2 -7514.598633 +#define ADD_Y1 -1204.051880 +#define ADD_Y2 -1150.448853 +#define ADD_Z1 476.800476 +#define ADD_Z2 476.796570 + +#define NEF_X -7445 +#define NEF_Y -1332 +#define NEF_Z 536 + +#define HIDE_X -7592 +#define HIDE_Y -1264 +#define HIDE_Z 481 + +#define SPELL_SHADOWBOLT 21077 +#define SPELL_FEAR 26070 + +//This script is complicated +//Instead of morphing Victor Nefarius we will have him control phase 1 +//And then have him spawn "Nefarian" for phase 2 +//When phase 2 starts Victor Nefarius will go into hiding and stop attacking +//If Nefarian despawns because he killed the players then this guy will EnterEvadeMode +//and allow players to start the event over +//If nefarian dies then he will kill himself then he will kill himself in his hiding place +//To prevent players from doing the event twice + +struct MANGOS_DLL_DECL boss_victor_nefariusAI : public ScriptedAI +{ + boss_victor_nefariusAI(Creature *c) : ScriptedAI(c) + { + NefarianGUID = 0; + Reset(); + srand(time(NULL)); + switch (rand()%20) + { + case 0: + DrakType1 = CREATURE_BRONZE_DRAKANOID; + DrakType2 = CREATURE_BLUE_DRAKANOID; + break; + case 1: + DrakType1 = CREATURE_BRONZE_DRAKANOID; + DrakType2 = CREATURE_RED_DRAKANOID; + break; + case 2: + DrakType1 = CREATURE_BRONZE_DRAKANOID; + DrakType2 = CREATURE_GREEN_DRAKANOID; + break; + case 3: + DrakType1 = CREATURE_BRONZE_DRAKANOID; + DrakType2 = CREATURE_BLACK_DRAKANOID; + break; + case 4: + DrakType1 = CREATURE_BLUE_DRAKANOID; + DrakType2 = CREATURE_BRONZE_DRAKANOID; + break; + case 5: + DrakType1 = CREATURE_BLUE_DRAKANOID; + DrakType2 = CREATURE_RED_DRAKANOID; + break; + case 6: + DrakType1 = CREATURE_BLUE_DRAKANOID; + DrakType2 = CREATURE_GREEN_DRAKANOID; + break; + case 7: + DrakType1 = CREATURE_BLUE_DRAKANOID; + DrakType2 = CREATURE_BLACK_DRAKANOID; + break; + case 8: + DrakType1 = CREATURE_RED_DRAKANOID; + DrakType2 = CREATURE_BRONZE_DRAKANOID; + break; + case 9: + DrakType1 = CREATURE_RED_DRAKANOID; + DrakType2 = CREATURE_BLUE_DRAKANOID; + break; + case 10: + DrakType1 = CREATURE_RED_DRAKANOID; + DrakType2 = CREATURE_GREEN_DRAKANOID; + break; + case 11: + DrakType1 = CREATURE_RED_DRAKANOID; + DrakType2 = CREATURE_BLACK_DRAKANOID; + break; + case 12: + DrakType1 = CREATURE_GREEN_DRAKANOID; + DrakType2 = CREATURE_BRONZE_DRAKANOID; + break; + case 13: + DrakType1 = CREATURE_GREEN_DRAKANOID; + DrakType2 = CREATURE_BLUE_DRAKANOID; + break; + case 14: + DrakType1 = CREATURE_GREEN_DRAKANOID; + DrakType2 = CREATURE_RED_DRAKANOID; + break; + case 15: + DrakType1 = CREATURE_GREEN_DRAKANOID; + DrakType2 = CREATURE_BLACK_DRAKANOID; + break; + case 16: + DrakType1 = CREATURE_BLACK_DRAKANOID; + DrakType2 = CREATURE_BRONZE_DRAKANOID; + break; + case 17: + DrakType1 = CREATURE_BLACK_DRAKANOID; + DrakType2 = CREATURE_BLUE_DRAKANOID; + break; + case 18: + DrakType1 = CREATURE_BLACK_DRAKANOID; + DrakType2 = CREATURE_GREEN_DRAKANOID; + break; + case 19: + DrakType1 = CREATURE_BLACK_DRAKANOID; + DrakType2 = CREATURE_RED_DRAKANOID; + break; + } + } + + uint32 SpawnedAdds; + uint32 AddSpawnTimer; + uint32 ShadowBoltTimer; + uint32 FearTimer; + uint32 MindControlTimer; + uint32 ResetTimer; + uint32 DrakType1; + uint32 DrakType2; + uint64 NefarianGUID; + uint32 NefCheckTime; + + void Reset() + { + SpawnedAdds = 0; + AddSpawnTimer = 10000; + ShadowBoltTimer = 5000; + FearTimer = 8000; + ResetTimer = 900000; //On official it takes him 15 minutes(900 seconds) to reset. We are only doing 1 minute to make testing easier + NefarianGUID = 0; + NefCheckTime = 2000; + + m_creature->SetUInt32Value(UNIT_NPC_FLAGS,1); + m_creature->setFaction(35); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void BeginEvent(Player* target) + { + DoYell(SAY_GAMESBEGIN_2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_GAMESBEGIN); + + //MaNGOS::Singleton::Instance().GetMap(m_creature->GetMapId(), m_creature)->GetPlayers().begin(); + /* + list ::iterator i = MapManager::Instance().GetMap(m_creature->GetMapId(), m_creature)->GetPlayers().begin(); + + for (i = MapManager::Instance().GetMap(m_creature->GetMapId(), m_creature)->GetPlayers().begin(); i != MapManager::Instance().GetMap(m_creature->GetMapId(), m_creature)->GetPlayers().end(); ++i) + { + AttackStart((*i)); + } + */ + m_creature->SetUInt32Value(UNIT_NPC_FLAGS,0); + m_creature->setFaction(103); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + AttackStart(target); + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + //We simply use this function to find players until we can use Map->GetPlayers() + + if (who && who->GetTypeId() == TYPEID_PLAYER && m_creature->IsHostileTo(who)) + { + //Add them to our threat list + m_creature->AddThreat(who,0.0f); + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Only do this if we haven't spawned nef yet + if (SpawnedAdds < 42) + { + //ShadowBoltTimer + if (ShadowBoltTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + DoCast(target,SPELL_SHADOWBOLT); + + ShadowBoltTimer = 3000 + (rand()%7000); + }else ShadowBoltTimer -= diff; + + //FearTimer + if (FearTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + DoCast(target,SPELL_FEAR); + + FearTimer = 10000 + (rand()%10000); + }else FearTimer -= diff; + + //Add spawning mechanism + if (AddSpawnTimer < diff) + { + //Spawn 2 random types of creatures at the 2 locations + uint32 CreatureID; + Creature* Spawned = NULL; + Unit* target = NULL; + + //1 in 3 chance it will be a chromatic + if (rand()%3 == 0) + CreatureID = CREATURE_CHROMATIC_DRAKANOID; + else CreatureID = DrakType1; + + SpawnedAdds++; + + //Spawn creature and force it to start attacking a random target + Spawned = m_creature->SummonCreature(CreatureID,ADD_X1,ADD_Y1,ADD_Z1,5.000,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target && Spawned) + { + Spawned->AI()->AttackStart(target); + Spawned->setFaction(103); + } + + //1 in 3 chance it will be a chromatic + if (rand()%3 == 0) + CreatureID = CREATURE_CHROMATIC_DRAKANOID; + else CreatureID = DrakType2; + + SpawnedAdds++; + + target = NULL; + Spawned = NULL; + Spawned = m_creature->SummonCreature(CreatureID,ADD_X2,ADD_Y2,ADD_Z2,5.000,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target && Spawned) + { + Spawned->AI()->AttackStart(target); + Spawned->setFaction(103); + } + + //Begin phase 2 by spawning Nefarian and what not + if (SpawnedAdds >= 42) + { + //Teleport Victor Nefarius way out of the map + //MapManager::Instance().GetMap(m_creature->GetMapId(), m_creature)->CreatureRelocation(m_creature,0,0,-5000,0); + + //Inturrupt any spell casting + m_creature->InterruptNonMeleeSpells(false); + + //Root self + DoCast(m_creature,33356); + + //Make super invis + DoCast(m_creature,8149); + + //Teleport self to a hiding spot (this causes errors in the mangos log but no real issues) + m_creature->Relocate(HIDE_X,HIDE_Y,HIDE_Z,0); + m_creature->SendMonsterMove(HIDE_X,HIDE_Y,HIDE_Z,0,true,0); + m_creature->addUnitState(UNIT_STAT_FLEEING); + + //Spawn nef and have him attack a random target + Creature* Nefarian = NULL; + Nefarian = m_creature->SummonCreature(CREATURE_NEFARIAN,NEF_X,NEF_Y,NEF_Z,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,120000); + target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target && Nefarian) + { + Nefarian->AI()->AttackStart(target); + Nefarian->setFaction(103); + NefarianGUID = Nefarian->GetGUID(); + } + else DoYell("UNABLE TO SPAWN NEF PROPERLY",LANG_UNIVERSAL,NULL); + } + + AddSpawnTimer = 4000; + }else AddSpawnTimer -= diff; + } + else if (NefarianGUID) + { + if (NefCheckTime < diff) + { + Unit* Nefarian = NULL; + Nefarian = Unit::GetUnit((*m_creature),NefarianGUID); + + //If nef is dead then we die to so the players get out of combat + //and cannot repeat the event + if (!Nefarian || !Nefarian->isAlive()) + { + NefarianGUID = 0; + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + NefCheckTime = 2000; + }else NefCheckTime -= diff; + } + } +}; + +CreatureAI* GetAI_boss_victor_nefarius(Creature *_Creature) +{ + return new boss_victor_nefariusAI (_Creature); +} + +bool GossipHello_boss_victor_nefarius(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM_1 , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(7134,_Creature->GetGUID()); + return true; +} + +bool GossipSelect_boss_victor_nefarius(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(7198, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->SEND_GOSSIP_MENU(7199, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->CLOSE_GOSSIP_MENU(); + _Creature->MonsterSay(SAY_GAMESBEGIN_1,LANG_UNIVERSAL,0); + ((boss_victor_nefariusAI*)_Creature->AI())->BeginEvent(player); + break; + } + return true; +} + +void AddSC_boss_victor_nefarius() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_victor_nefarius"; + newscript->GetAI = GetAI_boss_victor_nefarius; + newscript->pGossipHello = &GossipHello_boss_victor_nefarius; + newscript->pGossipSelect = &GossipSelect_boss_victor_nefarius; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blackwing_lair/instance_blackwing_lair.cpp b/src/bindings/scripts/scripts/zone/blackwing_lair/instance_blackwing_lair.cpp new file mode 100644 index 00000000000..7bdd3250442 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blackwing_lair/instance_blackwing_lair.cpp @@ -0,0 +1,8 @@ +/* ScriptData +SDName: Instance_Blackwing_Lair +SD%Complete: 0 +SDComment: +SDCategory: Blackwing Lair +EndScriptData */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/scripts/zone/blades_edge_mountains/blades_edge_mountains.cpp b/src/bindings/scripts/scripts/zone/blades_edge_mountains/blades_edge_mountains.cpp new file mode 100644 index 00000000000..c7cb250eab7 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blades_edge_mountains/blades_edge_mountains.cpp @@ -0,0 +1,441 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Blades_Edge_Mountains +SD%Complete: 90 +SDComment: Quest support: 10503, 10504, 10556, 10609, 10682, 10980. Ogri'la->Skettis Flight. (npc_daranelle needs bit more work before consider complete) +SDCategory: Blade's Edge Mountains +EndScriptData */ + +/* ContentData +mobs_bladespire_ogre +mobs_nether_drake +npc_daranelle +npc_overseer_nuaar +npc_saikkal_the_elder +npc_skyguard_handler_irena +EndContentData */ + +#include "precompiled.h" + +/*###### +## mobs_bladespire_ogre +######*/ + +//TODO: add support for quest 10512 + creature abilities +struct MANGOS_DLL_DECL mobs_bladespire_ogreAI : public ScriptedAI +{ + mobs_bladespire_ogreAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + } + + void Aggro(Unit* who) + { + } + + void JustDied(Unit* Killer) + { + if (Killer->GetTypeId() == TYPEID_PLAYER) + ((Player*)Killer)->KilledMonster(19995, m_creature->GetGUID()); + } +}; +CreatureAI* GetAI_mobs_bladespire_ogre(Creature *_Creature) +{ + return new mobs_bladespire_ogreAI (_Creature); +} + +/*###### +## mobs_nether_drake +######*/ + +#define SAY_NIHIL_1 "Muahahahaha! You fool! You've released me from my banishment in the interstices between space and time!" +#define SAY_NIHIL_2 "All of Draenor shall quick beneath my feet! I will destroy this world and reshape it in my image!" +#define SAY_NIHIL_3 "Where shall I begin? I cannot bother myself with a worm such as yourself. There is a world to be conquered!" +#define SAY_NIHIL_4 "No doubt the fools that banished me are long dead. I shall take wing survey my demense. Pray to whatever gods you hold dear that we do not meet again." +#define SAY_NIHIL_INTERRUPT "NOOOOooooooo!" + +#define ENTRY_WHELP 20021 +#define ENTRY_PROTO 21821 +#define ENTRY_ADOLE 21817 +#define ENTRY_MATUR 21820 +#define ENTRY_NIHIL 21823 + +#define SPELL_T_PHASE_MODULATOR 37573 + +#define SPELL_ARCANE_BLAST 38881 +#define SPELL_MANA_BURN 38884 +#define SPELL_INTANGIBLE_PRESENCE 36513 + +struct MANGOS_DLL_DECL mobs_nether_drakeAI : public ScriptedAI +{ + mobs_nether_drakeAI(Creature *c) : ScriptedAI(c) {Reset();} + + bool IsNihil; + uint32 NihilSpeech_Timer; + uint32 NihilSpeech_Phase; + + uint32 ArcaneBlast_Timer; + uint32 ManaBurn_Timer; + uint32 IntangiblePresence_Timer; + + void Reset() + { + NihilSpeech_Timer = 2000; + IsNihil = false; + if( m_creature->GetEntry() == ENTRY_NIHIL ) + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + IsNihil = true; + } + NihilSpeech_Phase = 1; + + ArcaneBlast_Timer = 7500; + ManaBurn_Timer = 10000; + IntangiblePresence_Timer = 15000; + } + + void Aggro(Unit* who) { } + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + if( spell->Id == SPELL_T_PHASE_MODULATOR && caster->GetTypeId() == TYPEID_PLAYER ) + { + uint32 cEntry = 0; + + switch( m_creature->GetEntry() ) + { + case ENTRY_WHELP: + switch(rand()%4) + { + case 0: cEntry = ENTRY_PROTO; break; + case 1: cEntry = ENTRY_ADOLE; break; + case 2: cEntry = ENTRY_MATUR; break; + case 3: cEntry = ENTRY_NIHIL; break; + } + break; + case ENTRY_PROTO: + switch(rand()%3) + { + case 0: cEntry = ENTRY_ADOLE; break; + case 1: cEntry = ENTRY_MATUR; break; + case 2: cEntry = ENTRY_NIHIL; break; + } + break; + case ENTRY_ADOLE: + switch(rand()%3) + { + case 0: cEntry = ENTRY_PROTO; break; + case 1: cEntry = ENTRY_MATUR; break; + case 2: cEntry = ENTRY_NIHIL; break; + } + break; + case ENTRY_MATUR: + switch(rand()%3) + { + case 0: cEntry = ENTRY_PROTO; break; + case 1: cEntry = ENTRY_ADOLE; break; + case 2: cEntry = ENTRY_NIHIL; break; + } + break; + case ENTRY_NIHIL: + if( NihilSpeech_Phase ) + { + DoYell(SAY_NIHIL_INTERRUPT,LANG_UNIVERSAL,NULL); + IsNihil = false; + switch(rand()%3) + { + case 0: cEntry = ENTRY_PROTO; break; + case 1: cEntry = ENTRY_ADOLE; break; + case 2: cEntry = ENTRY_MATUR; break; + } + } + break; + } + + if( cEntry ) + { + m_creature->UpdateEntry(cEntry); + + if( cEntry == ENTRY_NIHIL ) + { + m_creature->InterruptNonMeleeSpells(true); + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(); + InCombat = false; + Reset(); + } + } + } + } + + void UpdateAI(const uint32 diff) + { + if( IsNihil ) + { + if( NihilSpeech_Phase ) + { + if(NihilSpeech_Timer <= diff) + { + switch( NihilSpeech_Phase ) + { + case 1: + DoSay(SAY_NIHIL_1,LANG_UNIVERSAL,NULL); + ++NihilSpeech_Phase; + break; + case 2: + DoSay(SAY_NIHIL_2,LANG_UNIVERSAL,NULL); + ++NihilSpeech_Phase; + break; + case 3: + DoSay(SAY_NIHIL_3,LANG_UNIVERSAL,NULL); + ++NihilSpeech_Phase; + break; + case 4: + DoSay(SAY_NIHIL_4,LANG_UNIVERSAL,NULL); + ++NihilSpeech_Phase; + break; + case 5: + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + // + MOVEMENTFLAG_LEVITATING + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + //then take off to random location. creature is initially summoned, so don't bother do anything else. + m_creature->GetMotionMaster()->MovePoint(0, m_creature->GetPositionX()+100, m_creature->GetPositionY(), m_creature->GetPositionZ()+100); + NihilSpeech_Phase = 0; + break; + } + NihilSpeech_Timer = 5000; + }else NihilSpeech_Timer -=diff; + } + return; //anything below here is not interesting for Nihil, so skip it + } + + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( IntangiblePresence_Timer <= diff ) + { + DoCast(m_creature->getVictim(),SPELL_INTANGIBLE_PRESENCE); + IntangiblePresence_Timer = 15000+rand()%15000; + }else IntangiblePresence_Timer -= diff; + + if( ManaBurn_Timer <= diff ) + { + Unit* target = m_creature->getVictim(); + if( target && target->getPowerType() == POWER_MANA ) + DoCast(target,SPELL_MANA_BURN); + ManaBurn_Timer = 8000+rand()%8000; + }else ManaBurn_Timer -= diff; + + if( ArcaneBlast_Timer <= diff ) + { + DoCast(m_creature->getVictim(),SPELL_ARCANE_BLAST); + ArcaneBlast_Timer = 2500+rand()%5000; + }else ArcaneBlast_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_mobs_nether_drake(Creature *_Creature) +{ + return new mobs_nether_drakeAI (_Creature); +} + +/*###### +## npc_daranelle +######*/ + +struct MANGOS_DLL_DECL npc_daranelleAI : public ScriptedAI +{ + npc_daranelleAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + } + + void Aggro(Unit* who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->GetTypeId() == TYPEID_PLAYER) + { + if(who->HasAura(36904,0)) + { + DoSay("Good $N, you are under the spell's influence. I must analyze it quickly, then we can talk.",LANG_COMMON,who); + //TODO: Move the below to updateAI and run if this statement == true + ((Player*)who)->KilledMonster(21511, m_creature->GetGUID()); + ((Player*)who)->RemoveAurasDueToSpell(36904); + } + } + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + //Begin melee attack if we are within range + DoStartAttackAndMovement(who); + } + } + } +}; + +CreatureAI* GetAI_npc_daranelle(Creature *_Creature) +{ + return new npc_daranelleAI (_Creature); +} + +/*###### +## npc_overseer_nuaar +######*/ + +bool GossipHello_npc_overseer_nuaar(Player *player, Creature *_Creature) +{ + if (player->GetQuestStatus(10682) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Overseer, I am here to negotiate on behalf of the Cenarion Expedition.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(10532, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_overseer_nuaar(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + player->SEND_GOSSIP_MENU(10533, _Creature->GetGUID()); + player->AreaExploredOrEventHappens(10682); + } + return true; +} + +/*###### +## npc_saikkal_the_elder +######*/ + +bool GossipHello_npc_saikkal_the_elder(Player *player, Creature *_Creature) +{ + if (player->GetQuestStatus(10980) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Yes... yes, it's me.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(10794, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_saikkal_the_elder(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "Yes elder. Tell me more of the book.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(10795, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->TalkedToCreature(_Creature->GetEntry(), _Creature->GetGUID()); + player->SEND_GOSSIP_MENU(10796, _Creature->GetGUID()); + break; + } + return true; +} + +/*###### +## npc_skyguard_handler_irena +######*/ + +#define GOSSIP_SKYGUARD "Fly me to Skettis please" + +bool GossipHello_npc_skyguard_handler_irena(Player *player, Creature *_Creature ) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetReputationRank(1031) >= REP_HONORED) + player->ADD_GOSSIP_ITEM( 2, GOSSIP_SKYGUARD, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_skyguard_handler_irena(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + player->CLOSE_GOSSIP_MENU(); + + std::vector nodes; + + nodes.resize(2); + nodes[0] = 172; //from ogri'la + nodes[1] = 171; //end at skettis + player->ActivateTaxiPathTo(nodes); //TaxiPath 706 + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_blades_edge_mountains() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mobs_bladespire_ogre"; + newscript->GetAI = GetAI_mobs_bladespire_ogre; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mobs_nether_drake"; + newscript->GetAI = GetAI_mobs_nether_drake; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_daranelle"; + newscript->GetAI = GetAI_npc_daranelle; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_overseer_nuaar"; + newscript->pGossipHello = &GossipHello_npc_overseer_nuaar; + newscript->pGossipSelect = &GossipSelect_npc_overseer_nuaar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_saikkal_the_elder"; + newscript->pGossipHello = &GossipHello_npc_saikkal_the_elder; + newscript->pGossipSelect = &GossipSelect_npc_saikkal_the_elder; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_skyguard_handler_irena"; + newscript->pGossipHello = &GossipHello_npc_skyguard_handler_irena; + newscript->pGossipSelect = &GossipSelect_npc_skyguard_handler_irena; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blasted_lands/blasted_lands.cpp b/src/bindings/scripts/scripts/zone/blasted_lands/blasted_lands.cpp new file mode 100644 index 00000000000..4c84994c5b6 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blasted_lands/blasted_lands.cpp @@ -0,0 +1,159 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Blasted_Lands +SD%Complete: 90 +SDComment: Quest support: 2784, 2801, 3628. Missing some texts for Fallen Hero. Teleporter to Rise of the Defiler missing group support. +SDCategory: Blasted Lands +EndScriptData */ + +/* ContentData +npc_deathly_usher +npc_fallen_hero_of_horde +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_deathly_usher +######*/ + +#define GOSSIP_ITEM_USHER "I wish to to visit the Rise of the Defiler." + +#define SPELL_TELEPORT_SINGLE 12885 +#define SPELL_TELEPORT_SINGLE_IN_GROUP 13142 +#define SPELL_TELEPORT_GROUP 27686 + +bool GossipHello_npc_deathly_usher(Player *player, Creature *_Creature) +{ + if(player->GetQuestStatus(3628) == QUEST_STATUS_INCOMPLETE && player->HasItemCount(10757, 1)) + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM_USHER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_deathly_usher(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if(action = GOSSIP_ACTION_INFO_DEF) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->CastSpell(player, SPELL_TELEPORT_SINGLE, true); + } + + return true; +} + +/*###### +## npc_fallen_hero_of_horde +######*/ + +#define GOSSIP_ITEM_FALLEN "Continue..." + +#define GOSSIP_ITEM_FALLEN1 "What could be worse than death?" +#define GOSSIP_ITEM_FALLEN2 "Subordinates?" +#define GOSSIP_ITEM_FALLEN3 "What are the stones of binding?" +#define GOSSIP_ITEM_FALLEN4 "You can count on me, Hero" +#define GOSSIP_ITEM_FALLEN5 "I shall" + +bool GossipHello_npc_fallen_hero_of_horde(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(2784) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Why are you here?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + if (player->GetQuestStatus(2801) == QUEST_STATUS_INCOMPLETE && player->GetTeam() == HORDE) + player->ADD_GOSSIP_ITEM( 0, "Continue story...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + + if (player->GetQuestStatus(2801) == QUEST_STATUS_INCOMPLETE && player->GetTeam() == ALLIANCE) + player->ADD_GOSSIP_ITEM( 0, "Why are you here?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_fallen_hero_of_horde(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_FALLEN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(1392, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+11: + player->SEND_GOSSIP_MENU(1411, _Creature->GetGUID()); + if (player->GetQuestStatus(2784) == QUEST_STATUS_INCOMPLETE) + player->AreaExploredOrEventHappens(2784); + if (player->GetTeam() == ALLIANCE) + { + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_FALLEN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(1411, _Creature->GetGUID()); + } + break; + + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_FALLEN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 21); + player->SEND_GOSSIP_MENU(1451, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+21: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_FALLEN1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 22); + player->SEND_GOSSIP_MENU(1452, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+22: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_FALLEN2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 23); + player->SEND_GOSSIP_MENU(1453, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+23: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_FALLEN3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 24); + player->SEND_GOSSIP_MENU(1454, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+24: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_FALLEN4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 25); + player->SEND_GOSSIP_MENU(1455, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+25: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_FALLEN5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 26); + player->SEND_GOSSIP_MENU(1456, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+26: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(2801); + break; + } + return true; +} + +void AddSC_blasted_lands() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_deathly_usher"; + newscript->pGossipHello = &GossipHello_npc_deathly_usher; + newscript->pGossipSelect = &GossipSelect_npc_deathly_usher; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_fallen_hero_of_horde"; + newscript->pGossipHello = &GossipHello_npc_fallen_hero_of_horde; + newscript->pGossipSelect = &GossipSelect_npc_fallen_hero_of_horde; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/blasted_lands/boss_kruul.cpp b/src/bindings/scripts/scripts/zone/blasted_lands/boss_kruul.cpp new file mode 100644 index 00000000000..fd8eb90ddf0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/blasted_lands/boss_kruul.cpp @@ -0,0 +1,182 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Kruul +SD%Complete: 100 +SDComment: Highlord Kruul are presumably no longer in-game on regular bases, however future events could bring him back. +SDCategory: Bosses +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWVOLLEY 21341 +#define SPELL_CLEAVE 20677 +#define SPELL_THUNDERCLAP 23931 +#define SPELL_TWISTEDREFLECTION 21063 +#define SPELL_VOIDBOLT 21066 +#define SPELL_RAGE 21340 +#define SPELL_CAPTURESOUL 21054 + +struct MANGOS_DLL_DECL boss_kruulAI : public ScriptedAI +{ + boss_kruulAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowVolley_Timer; + uint32 Cleave_Timer; + uint32 ThunderClap_Timer; + uint32 TwistedReflection_Timer; + uint32 VoidBolt_Timer; + uint32 Rage_Timer; + uint32 Hound_Timer; + int Rand; + int RandX; + int RandY; + Creature* Summoned; + + void Reset() + { + ShadowVolley_Timer = 10000; + Cleave_Timer = 14000; + ThunderClap_Timer = 20000; + TwistedReflection_Timer = 25000; + VoidBolt_Timer = 30000; + Rage_Timer = 60000; //Cast rage after 1 minute + Hound_Timer = 8000; + } + + void Aggro(Unit *who) + { + } + + void KilledUnit() + { + // When a player, pet or totem gets killed, Lord Kazzak casts this spell to instantly regenerate 70,000 health. + DoCast(m_creature,SPELL_CAPTURESOUL); + + } + + void SummonHounds(Unit* victim) + { + Rand = rand()%10; + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = rand()%10; + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + Summoned = DoSpawnCreature(19207, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 300000); + if(Summoned) + ((CreatureAI*)Summoned->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ShadowVolley_Timer + if (ShadowVolley_Timer < diff) + { + if (rand()%100 < 46) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWVOLLEY); + } + + ShadowVolley_Timer = 5000; + }else ShadowVolley_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + if (rand()%100 < 50) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + } + + Cleave_Timer = 10000; + }else Cleave_Timer -= diff; + + //ThunderClap_Timer + if (ThunderClap_Timer < diff) + { + if (rand()%100 < 20) + { + DoCast(m_creature->getVictim(),SPELL_THUNDERCLAP); + } + + ThunderClap_Timer = 12000; + }else ThunderClap_Timer -= diff; + + //TwistedReflection_Timer + if (TwistedReflection_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_TWISTEDREFLECTION); + TwistedReflection_Timer = 30000; + }else TwistedReflection_Timer -= diff; + + //VoidBolt_Timer + if (VoidBolt_Timer < diff) + { + if (rand()%100 < 40) + { + DoCast(m_creature->getVictim(),SPELL_VOIDBOLT); + } + + VoidBolt_Timer = 18000; + }else VoidBolt_Timer -= diff; + + //Rage_Timer + if (Rage_Timer < diff) + { + DoCast(m_creature,SPELL_RAGE); + Rage_Timer = 70000; + }else Rage_Timer -= diff; + + //Hound_Timer + if (Hound_Timer < diff) + { + SummonHounds(m_creature->getVictim()); + SummonHounds(m_creature->getVictim()); + SummonHounds(m_creature->getVictim()); + + Hound_Timer = 45000; + }else Hound_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_kruul(Creature *_Creature) +{ + return new boss_kruulAI (_Creature); +} + +void AddSC_boss_kruul() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_kruul"; + newscript->GetAI = GetAI_boss_kruul; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/bloodmyst_isle/bloodmyst_isle.cpp b/src/bindings/scripts/scripts/zone/bloodmyst_isle/bloodmyst_isle.cpp new file mode 100644 index 00000000000..254d5082797 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/bloodmyst_isle/bloodmyst_isle.cpp @@ -0,0 +1,141 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Bloodmyst_Isle +SD%Complete: 80 +SDComment: Quest support: 9670, 9756(gossip items text needed). +SDCategory: Bloodmyst Isle +EndScriptData */ + +/* ContentData +mob_webbed_creature +npc_captured_sunhawk_agent +EndContentData */ + +#include "precompiled.h" + +/*###### +## mob_webbed_creature +######*/ + +//possible creatures to be spawned +const uint32 possibleSpawns[32] = {17322, 17661, 17496, 17522, 17340, 17352, 17333, 17524, 17654, 17348, 17339, 17345, 17359, 17353, 17336, 17550, 17330, 17701, 17321, 17680, 17325, 17320, 17683, 17342, 17715, 17334, 17341, 17338, 17337, 17346, 17344, 17327}; + +struct MANGOS_DLL_DECL mob_webbed_creatureAI : public ScriptedAI +{ + mob_webbed_creatureAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + } + + void Aggro(Unit* who) + { + } + + void JustDied(Unit* Killer) + { + uint32 spawnCreatureID; + + switch(rand()%3) + { + case 0: + spawnCreatureID = 17681; + if (Killer->GetTypeId() == TYPEID_PLAYER) + ((Player*)Killer)->KilledMonster(spawnCreatureID, m_creature->GetGUID()); + break; + case 1: + case 2: + spawnCreatureID = possibleSpawns[rand()%31]; + break; + } + + if(spawnCreatureID) + DoSpawnCreature(spawnCreatureID,0,0,0,m_creature->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000); + } +}; +CreatureAI* GetAI_mob_webbed_creature(Creature *_Creature) +{ + return new mob_webbed_creatureAI (_Creature); +} + +/*###### +## npc_captured_sunhawk_agent +######*/ + +#define C_SUNHAWK_TRIGGER 17974 + +bool GossipHello_npc_captured_sunhawk_agent(Player *player, Creature *_Creature) +{ + if (player->HasAura(31609,1) && player->GetQuestStatus(9756) == QUEST_STATUS_INCOMPLETE) + { + player->ADD_GOSSIP_ITEM( 0, "[PH] ", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(9136, _Creature->GetGUID()); + } + else + player->SEND_GOSSIP_MENU(9134, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_captured_sunhawk_agent(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "[PH] ", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(9137, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "[PH] ", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->SEND_GOSSIP_MENU(9138, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, "[PH] ", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + player->SEND_GOSSIP_MENU(9139, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM( 0, "[PH] ", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(9140, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->ADD_GOSSIP_ITEM( 0, "[PH] ", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+6); + player->SEND_GOSSIP_MENU(9141, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + player->CLOSE_GOSSIP_MENU(); + player->TalkedToCreature(C_SUNHAWK_TRIGGER, _Creature->GetGUID()); + break; + } + return true; +} + +void AddSC_bloodmyst_isle() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mob_webbed_creature"; + newscript->GetAI = GetAI_mob_webbed_creature; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_captured_sunhawk_agent"; + newscript->pGossipHello = &GossipHello_npc_captured_sunhawk_agent; + newscript->pGossipSelect = &GossipSelect_npc_captured_sunhawk_agent; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/burning_steppes/burning_steppes.cpp b/src/bindings/scripts/scripts/zone/burning_steppes/burning_steppes.cpp new file mode 100644 index 00000000000..0526163be56 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/burning_steppes/burning_steppes.cpp @@ -0,0 +1,157 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Burning_Steppes +SD%Complete: 100 +SDComment: Quest support: 4224, 4866 +SDCategory: Burning Steppes +EndScriptData */ + +/* ContentData +npc_ragged_john +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_ragged_john +######*/ + +struct MANGOS_DLL_DECL npc_ragged_johnAI : public ScriptedAI +{ + npc_ragged_johnAI(Creature *c) : ScriptedAI(c) { Reset(); } + + void Reset() {} + + void MoveInLineOfSight(Unit *who) + { + if( who->HasAura(16468,0) ) + { + if( who->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(who, 15) && who->isInAccessablePlaceFor(m_creature) ) + { + DoCast(who,16472); + ((Player*)who)->AreaExploredOrEventHappens(4866); + } + } + + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if (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) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void Aggro(Unit *who) {} +}; + +CreatureAI* GetAI_npc_ragged_john(Creature *_Creature) +{ + return new npc_ragged_johnAI (_Creature); +} + +bool GossipHello_npc_ragged_john(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(4224) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Official buisness, John. I need some information about Marsha Windsor. Tell me about the last time you saw him.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(2713, _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_ragged_john(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, "So what did you do?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(2714, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "Start making sense, dwarf. I don't want to have anything to do with your cracker, your pappy, or any sort of 'discreditin'.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(2715, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "Ironfoe?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(2716, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, "Interesting... continue John.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->SEND_GOSSIP_MENU(2717, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM( 0, "So that's how Windsor died...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(2718, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->ADD_GOSSIP_ITEM( 0, "So how did he die?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(2719, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + player->ADD_GOSSIP_ITEM( 0, "Ok so where the hell is he? Wait a minute! Are you drunk?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(2720, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+7: + player->ADD_GOSSIP_ITEM( 0, "WHY is he in Blackrock Depths?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->SEND_GOSSIP_MENU(2721, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+8: + player->ADD_GOSSIP_ITEM( 0, "300? So the Dark Irons killed him and dragged him into the Depths?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->SEND_GOSSIP_MENU(2722, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+9: + player->ADD_GOSSIP_ITEM( 0, "Ahh... Ironfoe", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->SEND_GOSSIP_MENU(2723, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+10: + player->ADD_GOSSIP_ITEM( 0, "Thanks, Ragged John. Your story was very uplifting and informative", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(2725, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+11: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(4224); + break; + } + return true; +} + +void AddSC_burning_steppes() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_ragged_john"; + newscript->GetAI = GetAI_npc_ragged_john; + newscript->pGossipHello = &GossipHello_npc_ragged_john; + newscript->pGossipSelect = &GossipSelect_npc_ragged_john; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_aeonus.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_aeonus.cpp new file mode 100644 index 00000000000..89e9ee31dfe --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_aeonus.cpp @@ -0,0 +1,137 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Aeonus +SD%Complete: 100 +SDComment: +SDCategory: Caverns of Time, The Dark Portal +EndScriptData */ + +#include "precompiled.h" + +#define SAND_BREATH 31478 +#define TIME_STOP 31422 +#define FRENZY 19812 + +#define SAY_ENTER "The time has come to shatter this clockwork universe forever! Let us no longer be slaves of the hourglass! I warn you: those who do not embrace the greater path shall become victims of its passing!" +#define SAY_AGGRO "Let us see what fate lays in store..." +#define SAY_BANISH "Your time is up, slave of the past!" +#define SAY_SLAY1 "One less obstacle in our way!" +#define SAY_SLAY2 "No one can stop us! No one!" +#define SAY_DEATH "It is only a matter...of time." + +#define SOUND_ENTER 10400 +#define SOUND_AGGRO 10402 +#define SOUND_BANISH 10401 +#define SOUND_SLAY1 10403 +#define SOUND_SLAY2 10404 +#define SOUND_DEATH 10405 + +struct MANGOS_DLL_DECL boss_aeonusAI : public ScriptedAI +{ + boss_aeonusAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 SandBreath_Timer; + uint32 TimeStop_Timer; + uint32 Frenzy_Timer; + + void Reset() + { + SandBreath_Timer = 30000; + TimeStop_Timer = 40000; + Frenzy_Timer = 120000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + + void JustDied(Unit *victim) + { + //Just Died + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void KilledUnit(Unit *victim) + { + //Killed Unit + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Sand Breath + if (SandBreath_Timer < diff) + { + Unit* target = NULL; + target = m_creature->getVictim(); + if (target) + DoCast(target, SAND_BREATH); + SandBreath_Timer = 30000; + }else SandBreath_Timer -= diff; + + //Time Stop + if (TimeStop_Timer < diff) + { + DoYell(SAY_BANISH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_BANISH); + + DoCast(m_creature->getVictim(), TIME_STOP); + TimeStop_Timer = 40000; + }else TimeStop_Timer -= diff; + + //Frenzy + if (Frenzy_Timer < diff) + { + DoCast(m_creature, FRENZY); + Frenzy_Timer = 120000; + }else Frenzy_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_aeonus(Creature *_Creature) +{ + return new boss_aeonusAI (_Creature); +} + +void AddSC_boss_aeonus() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_aeonus"; + newscript->GetAI = GetAI_boss_aeonus; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp new file mode 100644 index 00000000000..1dcc7fcc5ee --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp @@ -0,0 +1,124 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Chrono_Lord_Deja +SD%Complete: 100 +SDComment: +SDCategory: Caverns of Time, The Dark Portal +EndScriptData */ + +#include "precompiled.h" + +#define ARCANE_BLAST 24857 +#define TIME_LAPSE 31467 +#define MAGNETIC_PULL 28337 //Not Implemented (Heroic mod) + +#define SAY_ENTER "Why do you aid the Magus? Just think of how many lives could be saved if the portal is never opened, if the resulting wars could be erased ..." +#define SAY_AGGRO "If you will not cease this foolish quest, then you will die!" +#define SAY_BANISH "You have outstayed your welcome, Timekeeper. Begone!" +#define SAY_SLAY1 "I told you it was a fool's quest!" +#define SAY_SLAY2 "Leaving so soon?" +#define SAY_DEATH "Time ... is on our side." + +#define SOUND_ENTER 10412 +#define SOUND_AGGRO 10414 +#define SOUND_BANISH 10413 +#define SOUND_SLAY1 10415 +#define SOUND_SLAY2 10416 +#define SOUND_DEATH 10417 + +struct MANGOS_DLL_DECL boss_chrono_lord_dejaAI : public ScriptedAI +{ + boss_chrono_lord_dejaAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ArcaneBlast_Timer; + uint32 TimeLapse_Timer; + + void Reset() + { + ArcaneBlast_Timer = 20000; + TimeLapse_Timer = 15000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Arcane Blast + if (ArcaneBlast_Timer < diff) + { + DoCast(m_creature->getVictim(), ARCANE_BLAST); + ArcaneBlast_Timer = 20000+rand()%5000; + }else ArcaneBlast_Timer -= diff; + + //Time Lapse + if (TimeLapse_Timer < diff) + { + DoYell(SAY_BANISH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_BANISH); + DoCast(m_creature, TIME_LAPSE); + TimeLapse_Timer = 15000+rand()%10000; + }else TimeLapse_Timer -= diff; + + DoMeleeAttackIfReady(); + + } +}; + +CreatureAI* GetAI_boss_chrono_lord_deja(Creature *_Creature) +{ + return new boss_chrono_lord_dejaAI (_Creature); +} + +void AddSC_boss_chrono_lord_deja() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_chrono_lord_deja"; + newscript->GetAI = GetAI_boss_chrono_lord_deja; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_temporus.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_temporus.cpp new file mode 100644 index 00000000000..c95657634cf --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/dark_portal/boss_temporus.cpp @@ -0,0 +1,166 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Temporus +SD%Complete: 100 +SDComment: +SDCategory: Caverns of Time, The Dark Portal +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_TAUNT 355 +#define SPELL_HASTE 31458 +#define SPELL_MORTAL_WOUND 28467 +#define SPELL_REFLECT 23920 //Not Implemented (Heroic mod) + +#define SAY_ENTER "Why do you persist? Surely you can see the futility of it all. It is not too late! You may still leave with your lives ..." +#define SAY_AGGRO "So be it ... you have been warned." +#define SAY_BANISH "Time... sands of time is run out for you." +#define SAY_SLAY1 "You should have left when you had the chance." +#define SAY_SLAY2 "Your days are done." +#define SAY_DEATH "My death means ... little." + +#define SOUND_ENTER 10442 +#define SOUND_AGGRO 10444 +#define SOUND_BANISH 10443 +#define SOUND_SLAY1 10445 +#define SOUND_SLAY2 10446 +#define SOUND_DEATH 10447 + +struct MANGOS_DLL_DECL boss_temporusAI : public ScriptedAI +{ + boss_temporusAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Haste_Timer; + uint32 SpellReflection_Timer; + + void Reset() + { + Haste_Timer = 20000; + SpellReflection_Timer = 40000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + //Despawn Time Keeper + if (who->GetTypeId() == TYPEID_UNIT) + { + if(((Creature*)who)->GetEntry() == 17918 && m_creature->IsWithinDistInMap(who,20)) + { + //This is the wrong yell & sound for despawning time keepers! + DoYell(SAY_ENTER, LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_ENTER); + + m_creature->DealDamage(who, who->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + if (!InCombat) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + } + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Check if we have a current target + if( m_creature->getVictim() && m_creature->isAlive()) + { + + //Attack Haste + if (Haste_Timer < diff) + { + DoCast(m_creature, SPELL_HASTE); + Haste_Timer = 20000+rand()%5000; + }else Haste_Timer -= diff; + + //Spell Reflection + if (SpellReflection_Timer < diff) + { + DoYell(SAY_BANISH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_BANISH); + + DoCast(m_creature, SPELL_REFLECT); + SpellReflection_Timer = 40000+rand()%10000; + }else SpellReflection_Timer -= diff; + + DoMeleeAttackIfReady(); + + } + } +}; + +CreatureAI* GetAI_boss_temporus(Creature *_Creature) +{ + return new boss_temporusAI (_Creature); +} + +void AddSC_boss_temporus() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_temporus"; + newscript->GetAI = GetAI_boss_temporus; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_archimonde.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_archimonde.cpp new file mode 100644 index 00000000000..d6e24a30a9a --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/boss_archimonde.cpp @@ -0,0 +1,788 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Archimonde +SD%Complete: 95 +SDComment: Doomfires not completely offlike due to core limitations for random moving. +SDCategory: Caverns of Time, Mount Hyjal +EndScriptData */ + +#include "precompiled.h" +#include "def_hyjal.h" +#include "SpellAuras.h" + +#define SPELL_DENOUEMENT_WISP 32124 +#define SPELL_ANCIENT_SPARK 39349 +#define SPELL_PROTECTION_OF_ELUNE 38528 + +#define SPELL_DRAIN_WORLD_TREE 39140 +#define SPELL_DRAIN_WORLD_TREE_2 39141 + +#define SPELL_FINGER_OF_DEATH 31984 +#define SPELL_HAND_OF_DEATH 35354 +#define SPELL_AIR_BURST 32014 +#define SPELL_GRIP_OF_THE_LEGION 31972 +#define SPELL_DOOMFIRE_SPAWN 32074 +#define SPELL_DOOMFIRE_VISUAL 42344 // This is actually a Zul'Aman spell, but the proper Doomfire spell sometimes freezes the server if a player stands in it for too long +#define SPELL_DOOMFIRE_DAMAGE 31944 +#define SPELL_SOUL_CHARGE_YELLOW 32045 +#define SPELL_SOUL_CHARGE_GREEN 32051 +#define SPELL_SOUL_CHARGE_RED 32052 +#define SPELL_UNLEASH_SOUL_YELLOW 32054 +#define SPELL_UNLEASH_SOUL_GREEN 32057 +#define SPELL_UNLEASH_SOUL_RED 32053 +#define SPELL_FEAR 31970 + +#define SAY_AGGRO "Your resistance is insignificant!" +#define SOUND_AGGRO 10987 + +#define SAY_DOOMFIRE1 "This world will burn!" +#define SOUND_DOOMFIRE1 10990 + +#define SAY_DOOMFIRE2 "Manach sheek-thrish!" +#define SOUND_DOOMFIRE2 11041 + +#define SAY_AIR_BURST "A-kreesh!" +#define SOUND_AIR_BURST 10989 + +#define SAY_AIR_BURST2 "Away vermin!" +#define SOUND_AIR_BURST2 11043 + +#define SAY_SLAY1 "All creation will be devoured!" +#define SOUND_SLAY1 11044 + +#define SAY_SLAY2 "Your soul will languish for eternity." +#define SOUND_SLAY2 10991 + +#define SAY_SLAY3 "I am the coming of the end!" +#define SOUND_SLAY3 11045 + +#define SAY_UNK1 "You are mine now." +#define SOUND_UNK1 10988 + +#define SAY_UNK2 "Bow to my will." +#define SOUND_UNK2 11042 + +#define SAY_ENRAGE "At last it is here. Mourn and lament the passing of all you have ever known and all that would have been! Akmin-kurai!" +#define SOUND_ENRAGE 10993 + +#define SAY_DEATH "No, it cannot be! Nooo!" +#define SOUND_DEATH 10992 + +#define CREATURE_ARCHIMONDE 17968 +#define CREATURE_DOOMFIRE 18095 +#define CREATURE_DOOMFIRE_TARGETING 18104 +#define CREATURE_ANCIENT_WISP 17946 +#define CREATURE_CHANNEL_TARGET 22418 + +#define NORDRASSIL_X 5503.713 +#define NORDRASSIL_Y -3523.436 +#define NORDRASSIL_Z 1608.781 + +struct mob_ancient_wispAI : public ScriptedAI +{ + mob_ancient_wispAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + uint64 ArchimondeGUID; + uint32 CheckTimer; + + void Reset() + { + ArchimondeGUID = 0; + CheckTimer = 1000; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + + void Aggro(Unit* who) {} + + void DamageTaken(Unit* done_by, uint32 &damage) { damage = 0; } + + void UpdateAI(const uint32 diff) + { + if(!ArchimondeGUID) + { + if(pInstance) + ArchimondeGUID = pInstance->GetData64(DATA_ARCHIMONDE); + } + + if(CheckTimer < diff) + { + if(ArchimondeGUID) + { + Unit* Archimonde = Unit::GetUnit((*m_creature), ArchimondeGUID); + if(Archimonde) + { + if((((Archimonde->GetHealth()*100) / Archimonde->GetMaxHealth()) < 2) || !Archimonde->isAlive()) + DoCast(m_creature, SPELL_DENOUEMENT_WISP); + else + DoCast(Archimonde, SPELL_ANCIENT_SPARK); + } + } + CheckTimer = 1000; + }else CheckTimer -= diff; + } +}; + +/* This script controls the Doomfire mob. Unlike the other Doomfire mob, this one does not stalk players. + Instead, this doomfire will simply stand in one place after spawning and deal damage to any players that + are within 3 yards. Another creature called Doomfire Targetting spawns this creature as well as stalks. */ +struct MANGOS_DLL_DECL mob_doomfireAI : public ScriptedAI +{ + mob_doomfireAI(Creature* c) : ScriptedAI(c) + { + Reset(); + } + + uint32 CheckTimer; + uint32 RefreshTimer; + + bool TargetSelected; + + uint64 ArchimondeGUID; + uint64 TargetGUID; + + void Reset() + { + CheckTimer = 5000; + RefreshTimer = 0; + + TargetSelected = false; + + ArchimondeGUID = 0; + TargetGUID = 0; + } + + void DamageTaken(Unit *done_by, uint32 &damage) { damage = 0; } + + void Aggro(Unit* who) { } + + void MoveInLineOfSight(Unit* who) + { + // Do not do anything if who does not exist, or we are refreshing our timer, or who is Doomfire, Archimonde or Doomfire targetting + if(!who || who == m_creature || RefreshTimer || who->GetEntry() == CREATURE_ANCIENT_WISP || + who->GetEntry() == CREATURE_ARCHIMONDE || who->GetEntry() == CREATURE_DOOMFIRE || + who->GetEntry() == CREATURE_DOOMFIRE_TARGETING || !who->isTargetableForAttack()) + return; + + if(m_creature->IsWithinDistInMap(who, 3)) + { + TargetSelected = true; + TargetGUID = who->GetGUID(); + RefreshTimer = 2000; + } + } + + void KilledUnit(Unit* victim) + { + bool suicide = true; + if(ArchimondeGUID) + { + Creature* Archimonde = ((Creature*)Unit::GetUnit((*m_creature), ArchimondeGUID)); + if(Archimonde && Archimonde->isAlive()) + { + suicide = false; + Archimonde->AI()->KilledUnit(victim); + } + } + + if(suicide) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + void UpdateAI(const uint32 diff) + { + if(RefreshTimer < diff) + RefreshTimer = 0; + else RefreshTimer -= diff; + + if(TargetSelected && TargetGUID) + { + Unit* target = Unit::GetUnit((*m_creature), TargetGUID); + if(target && target->isAlive()) + { + target->CastSpell(target, SPELL_DOOMFIRE_DAMAGE, true); + TargetGUID = 0; + TargetSelected = false; + } + } + + if(CheckTimer < diff) + { + if(ArchimondeGUID) + { + Unit* Archimonde = Unit::GetUnit((*m_creature), ArchimondeGUID); + if(!Archimonde || !Archimonde->isAlive()) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + CheckTimer = 5000; + } + else m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + }else CheckTimer -= diff; + } +}; + +/* This is the script for the Doomfire Targetting Mob. This mob simply follows players and/or travels in random directions and spawns the actual Doomfire which does damage to anyone that moves close. */ +struct MANGOS_DLL_DECL mob_doomfire_targettingAI : public ScriptedAI +{ + mob_doomfire_targettingAI(Creature* c) : ScriptedAI(c) + { + Reset(); + } + + uint32 ChangeTargetTimer; + uint32 SummonTimer; // This timer will serve as both a summon timer for the doomfire that does damage as well as to check on Archionde + + uint64 ArchimondeGUID; + + void Reset() + { + ChangeTargetTimer = 5000; + SummonTimer = 1000; + + ArchimondeGUID = 0; + } + + void Aggro(Unit* who) {} + + void MoveInLineOfSight(Unit* who) + { + // Do not do anything if who does not exist, or who is Doomfire, Archimonde or Doomfire targetting + if(!who || who == m_creature || who->GetEntry() == CREATURE_ARCHIMONDE + || who->GetEntry() == CREATURE_DOOMFIRE || who->GetEntry() == CREATURE_DOOMFIRE_TARGETING || !who->isTargetableForAttack()) + return; + + m_creature->AddThreat(who, 1.0f); + } + + void DamageTaken(Unit *done_by, uint32 &damage) { damage = 0; } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(SummonTimer < diff) + { + if(ArchimondeGUID) + { + Unit* Archimonde = Unit::GetUnit((*m_creature), ArchimondeGUID); + if(Archimonde && Archimonde->isAlive()) + { + Creature* Doomfire = DoSpawnCreature(CREATURE_DOOMFIRE, 0, 0, 2, 0, TEMPSUMMON_TIMED_DESPAWN, 30000); + if(Doomfire) + { + Doomfire->CastSpell(Doomfire, SPELL_DOOMFIRE_VISUAL, true); + ((mob_doomfireAI*)Doomfire->AI())->ArchimondeGUID = ArchimondeGUID; + Doomfire->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + SummonTimer = 500; + } + else + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + else + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + }else SummonTimer -= diff; + + if(ChangeTargetTimer < diff) + { + Unit* target = NULL; + switch(rand()%2) + { + case 0: // stalk player + target = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(target && target->isAlive()) + { + m_creature->AddThreat(target, m_creature->getThreatManager().getThreat(m_creature->getVictim())); + m_creature->GetMotionMaster()->MoveChase(target); + } + break; + + case 1: // random location + float x = 0; + float y = 0; + float z = 0; + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 40, x, y, z); + m_creature->GetMotionMaster()->MovePoint(0, x, y, z); + break; + } + ChangeTargetTimer = 5000; + }else ChangeTargetTimer -= diff; + } + +}; + +/* Finally, Archimonde's script. His script isn't extremely complex, most are simply spells on timers. + The only complicated aspect of the battle is Finger of Death and Doomfire, with Doomfire being the + hardest bit to code. Finger of Death is simply a distance check - if no one is in melee range, then + select a random target and cast the spell on them. However, if someone IS in melee range, and this + is NOT the main tank (creature's victim), then we aggro that player and they become the new victim. + For Doomfire, we summon a mob (Doomfire Targetting) that summons another mob (Doomfire every second) + Doomfire Targetting 'stalks' players whilst Doomfire damages player that are within range. */ + +// This is used to sort by distance in order to see who is the closest target, when checking for Finger of Death +struct TargetDistanceOrder : public std::binary_function +{ + 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)); + } +}; + +struct MANGOS_DLL_DECL boss_archimondeAI : public ScriptedAI +{ + boss_archimondeAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 DrainNordrassilTimer; + uint32 FearTimer; + uint32 AirBurstTimer; + uint32 GripOfTheLegionTimer; + uint32 DoomfireTimer; + uint32 SoulChargeTimer; + uint32 SoulChargeCount; + uint32 MeleeRangeCheckTimer; + uint32 HandOfDeathTimer; + uint32 SummonWispTimer; + uint32 WispCount; + uint32 EnrageTimer; + uint32 CheckDistanceTimer; + + bool Enraged; + bool BelowTenPercent; + bool HasProtected; + bool IsChanneling; + + void Reset() + { + if(pInstance) + pInstance->SetData(DATA_ARCHIMONDEEVENT, NOT_STARTED); + + DrainNordrassilTimer = 0; + FearTimer = 40000; + AirBurstTimer = 30000; + GripOfTheLegionTimer = 5000 + rand()%20000; + DoomfireTimer = 20000; + SoulChargeTimer = 2000 + rand()%27000; + SoulChargeCount = 0; + MeleeRangeCheckTimer = 15000; + HandOfDeathTimer = 2000; + WispCount = 0; // When ~30 wisps are summoned, Archimonde dies + EnrageTimer = 600000; // 10 minutes + CheckDistanceTimer = 30000; // This checks if he's too close to the World Tree (75 yards from a point on the tree), if true then he will enrage + + Enraged = false; + BelowTenPercent = false; + HasProtected = false; + IsChanneling = false; + } + + void Aggro(Unit *who) + { + m_creature->InterruptSpell(CURRENT_CHANNELED_SPELL); + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + DoZoneInCombat(); + + if(pInstance) + pInstance->SetData(DATA_ARCHIMONDEEVENT, IN_PROGRESS); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + case 2: + DoYell(SAY_SLAY3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY3); + break; + } + + if(victim && (victim->GetTypeId() == TYPEID_PLAYER)) + GainSoulCharge(((Player*)victim)); + } + + void GainSoulCharge(Player* victim) + { + switch(victim->getClass()) + { + case CLASS_PRIEST: + case CLASS_PALADIN: + case CLASS_WARLOCK: + victim->CastSpell(m_creature, SPELL_SOUL_CHARGE_RED, true); + break; + case CLASS_MAGE: + case CLASS_ROGUE: + case CLASS_WARRIOR: + victim->CastSpell(m_creature, SPELL_SOUL_CHARGE_YELLOW, true); + break; + case CLASS_DRUID: + case CLASS_SHAMAN: + case CLASS_HUNTER: + victim->CastSpell(m_creature, SPELL_SOUL_CHARGE_GREEN, true); + break; + } + + SoulChargeTimer = 2000 + rand()%28000; + ++SoulChargeCount; + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + + if(pInstance) + pInstance->SetData(DATA_ARCHIMONDEEVENT, DONE); + } + + bool CanUseFingerOfDeath() + { + // First we check if our current victim is in melee range or not. + Unit* victim = m_creature->getVictim(); + if(victim && m_creature->IsWithinDistInMap(victim, m_creature->GetAttackDistance(victim))) + return false; + + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + if(m_threatlist.empty()) + return false; + + std::list targets; + std::list::iterator itr = m_threatlist.begin(); + for( ; itr != m_threatlist.end(); ++itr) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid()); + if(pUnit && pUnit->isAlive()) + targets.push_back(pUnit); + } + + if(targets.empty()) + return false; + + targets.sort(TargetDistanceOrder(m_creature)); + Unit* target = targets.front(); + if(target) + { + if(!m_creature->IsWithinDistInMap(target, m_creature->GetAttackDistance(target))) + return true; // Cast Finger of Death + else // This target is closest, he is our new tank + m_creature->AddThreat(target, m_creature->getThreatManager().getThreat(m_creature->getVictim())); + } + + return false; + } + + void SummonDoomfire(Unit* target) + { + Creature* Doomfire = DoSpawnCreature(CREATURE_DOOMFIRE_TARGETING, rand()%30, rand()%30, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 30000); + if(Doomfire) + { + ((mob_doomfire_targettingAI*)Doomfire->AI())->ArchimondeGUID = m_creature->GetGUID(); + Doomfire->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + // Give Doomfire a taste of everyone in the threatlist = more targets to chase. + std::list::iterator itr; + for(itr = m_creature->getThreatManager().getThreatList().begin(); itr != m_creature->getThreatManager().getThreatList().end(); ++itr) + Doomfire->AddThreat(Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()), 1.0f); + Doomfire->setFaction(m_creature->getFaction()); + DoCast(Doomfire, SPELL_DOOMFIRE_SPAWN); + Doomfire->CastSpell(Doomfire, SPELL_DOOMFIRE_VISUAL, true); + if(target) + Doomfire->AI()->AttackStart(target); + + if(rand()%2 == 0) + { + DoYell(SAY_DOOMFIRE1, LANG_UNIVERSAL, m_creature); + DoPlaySoundToSet(m_creature, SOUND_DOOMFIRE1); + }else + { + DoYell(SAY_DOOMFIRE2, LANG_UNIVERSAL, m_creature); + DoPlaySoundToSet(m_creature, SOUND_DOOMFIRE2); + } + } + } + + void UnleashSoulCharge() + { + m_creature->InterruptNonMeleeSpells(false); + bool HasCast = false; + uint32 chargeSpell = 0; + uint32 unleashSpell = 0; + switch(rand()%3) + { + case 0: + chargeSpell = SPELL_SOUL_CHARGE_RED; + unleashSpell = SPELL_UNLEASH_SOUL_RED; + break; + case 1: + chargeSpell = SPELL_SOUL_CHARGE_YELLOW; + unleashSpell = SPELL_UNLEASH_SOUL_YELLOW; + break; + case 2: + chargeSpell = SPELL_SOUL_CHARGE_GREEN; + unleashSpell = SPELL_UNLEASH_SOUL_GREEN; + break; + } + if(m_creature->HasAura(chargeSpell, 0)) + { + m_creature->RemoveSingleAuraFromStack(chargeSpell, 0); + DoCast(m_creature->getVictim(), unleashSpell); + HasCast = true; + SoulChargeCount--; + } + if(HasCast) + SoulChargeTimer = 2000 + rand()%28000; + } + + void UpdateAI(const uint32 diff) + { + if(!InCombat) + { + if(pInstance) + { + // Do not let the raid skip straight to Archimonde. Visible and hostile ONLY if Azagalor is finished. + if((pInstance->GetData(DATA_AZGALOREVENT) < DONE) && ((m_creature->GetVisibility() != VISIBILITY_OFF) || (m_creature->getFaction() != 35))) + { + m_creature->SetVisibility(VISIBILITY_OFF); + m_creature->setFaction(35); + } + else if((pInstance->GetData(DATA_AZGALOREVENT) >= DONE) && ((m_creature->GetVisibility() != VISIBILITY_ON) || (m_creature->getFaction() == 35))) + { + m_creature->setFaction(1720); + m_creature->SetVisibility(VISIBILITY_ON); + } + } + + if(DrainNordrassilTimer < diff) + { + if(!IsChanneling) + { + Creature* Nordrassil = m_creature->SummonCreature(CREATURE_CHANNEL_TARGET, NORDRASSIL_X, NORDRASSIL_Y, NORDRASSIL_Z, 0, TEMPSUMMON_TIMED_DESPAWN, 1200000); + if(Nordrassil) + { + Nordrassil->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Nordrassil->SetUInt32Value(UNIT_FIELD_DISPLAYID, 11686); + DoCast(Nordrassil, SPELL_DRAIN_WORLD_TREE); + IsChanneling = true; + } + } + Creature* Nordrassil = m_creature->SummonCreature(CREATURE_CHANNEL_TARGET, NORDRASSIL_X, NORDRASSIL_Y, NORDRASSIL_Z, 0, TEMPSUMMON_TIMED_DESPAWN, 5000); + if(Nordrassil) + { + Nordrassil->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Nordrassil->SetUInt32Value(UNIT_FIELD_DISPLAYID, 11686); + Nordrassil->CastSpell(m_creature, SPELL_DRAIN_WORLD_TREE_2, true); + DrainNordrassilTimer = 1000; + } + }else DrainNordrassilTimer -= diff; + } + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 10) && !BelowTenPercent && !Enraged) + BelowTenPercent = true; + + if(!Enraged) + { + if(EnrageTimer < diff) + { + if((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) > 10) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + Enraged = true; + DoYell(SAY_ENRAGE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + } + }else EnrageTimer -= diff; + + if(CheckDistanceTimer < diff) + { // To simplify the check, we simply summon a creature in the location and then check how far we are from the creature + Creature* Check = m_creature->SummonCreature(CREATURE_CHANNEL_TARGET, NORDRASSIL_X, NORDRASSIL_Y, NORDRASSIL_Z, 0, TEMPSUMMON_TIMED_DESPAWN, 2000); + if(Check) + { + Check->SetVisibility(VISIBILITY_OFF); + if(m_creature->IsWithinDistInMap(Check, 75)) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + Enraged = true; + DoYell(SAY_ENRAGE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + } + } + CheckDistanceTimer = 5000; + }else CheckDistanceTimer -= diff; + } + + if(BelowTenPercent) + { + if(!HasProtected) + { + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + DoCast(m_creature->getVictim(), SPELL_PROTECTION_OF_ELUNE); + HasProtected = true; + Enraged = true; + } + + if(SummonWispTimer < diff) + { + Creature* Wisp = DoSpawnCreature(CREATURE_ANCIENT_WISP, rand()%40, rand()%40, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(Wisp) + { + Wisp->AI()->AttackStart(m_creature); + ((mob_ancient_wispAI*)Wisp->AI())->ArchimondeGUID = m_creature->GetGUID(); + } + SummonWispTimer = 1500; + ++WispCount; + }else SummonWispTimer -= diff; + + if(WispCount >= 30) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + if(Enraged) + { + if(HandOfDeathTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_HAND_OF_DEATH); + HandOfDeathTimer = 2000; + }else HandOfDeathTimer -= diff; + return; // Don't do anything after this point. + } + + if(SoulChargeCount) + { + if(SoulChargeTimer < diff) + UnleashSoulCharge(); + else SoulChargeTimer -= diff; + } + + if(GripOfTheLegionTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_GRIP_OF_THE_LEGION); + GripOfTheLegionTimer = 5000 + rand()%20000; + }else GripOfTheLegionTimer -= diff; + + if(AirBurstTimer < diff) + { + if(rand()%2 == 0) + { + DoYell(SAY_AIR_BURST, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AIR_BURST); + }else + { + DoYell(SAY_AIR_BURST2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AIR_BURST2); + } + + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_AIR_BURST); + AirBurstTimer = 25000 + rand()%15000; + }else AirBurstTimer -= diff; + + if(FearTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FEAR); + FearTimer = 40000; + }else FearTimer -= diff; + + if(DoomfireTimer < diff) + { + SummonDoomfire(SelectUnit(SELECT_TARGET_RANDOM, 1)); + DoomfireTimer = 40000; + }else DoomfireTimer -= diff; + + if(MeleeRangeCheckTimer < diff) + { + if(CanUseFingerOfDeath()) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_FINGER_OF_DEATH); + MeleeRangeCheckTimer = 1000; + } + + MeleeRangeCheckTimer = 5000; + }else MeleeRangeCheckTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_archimonde(Creature *_Creature) +{ + return new boss_archimondeAI (_Creature); +} + +CreatureAI* GetAI_mob_doomfire(Creature* _Creature) +{ + return new mob_doomfireAI(_Creature); +} + +CreatureAI* GetAI_mob_doomfire_targetting(Creature* _Creature) +{ + return new mob_doomfire_targettingAI(_Creature); +} + +CreatureAI* GetAI_mob_ancient_wisp(Creature* _Creature) +{ + return new mob_ancient_wispAI(_Creature); +} + +void AddSC_boss_archimonde() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_archimonde"; + newscript->GetAI = GetAI_boss_archimonde; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_doomfire"; + newscript->GetAI = GetAI_mob_doomfire; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_doomfire_targetting"; + newscript->GetAI = GetAI_mob_doomfire_targetting; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_ancient_wisp"; + newscript->GetAI = GetAI_mob_ancient_wisp; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/def_hyjal.h b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/def_hyjal.h new file mode 100644 index 00000000000..1e28156857f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/def_hyjal.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_HYJAL_H +#define DEF_HYJAL_H + +#define DATA_ANETHERON 1 +#define DATA_ANETHERONEVENT 2 +#define DATA_ARCHIMONDE 3 +#define DATA_ARCHIMONDEEVENT 4 +#define DATA_AZGALOR 5 +#define DATA_AZGALOREVENT 6 +#define DATA_JAINAPROUDMOORE 7 +#define DATA_KAZROGAL 8 +#define DATA_KAZROGALEVENT 9 +#define DATA_RAGEWINTERCHILL 10 +#define DATA_RAGEWINTERCHILLEVENT 11 +#define DATA_THRALL 12 +#define DATA_TYRANDEWHISPERWIND 13 +#define DATA_TRASH 14 +#define DATA_RESET_TRASH_COUNT 15 + +#define ERROR_INST_DATA "SD2: Instance data not set properly for Mount Hyjal. Encounters will be buggy" +#endif diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjal.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjal.cpp new file mode 100644 index 00000000000..14dbdf62736 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjal.cpp @@ -0,0 +1,217 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Hyjal +SD%Complete: 100 +SDComment: +SDCategory: Caverns of Time, Mount Hyjal +EndScriptData */ + +/* ContentData +npc_jaina_proudmoore +npc_thrall +npc_tyrande_whisperwind +EndContentData */ + +#include "precompiled.h" +#include "hyjalAI.h" + +#define GOSSIP_ITEM_BEGIN_ALLY "We are ready to defend the Alliance base." +#define GOSSIP_ITEM_ANETHERON "The defenses are holding up; we can continue." +#define GOSSIP_ITEM_RETREAT "We can't keep this up. Let's retreat!" + +#define GOSSIP_ITEM_BEGIN_HORDE "We're here to help! The Alliance are overrun." +#define GOSSIP_ITEM_AZGALOR "We're okay so far. Let's do this!" + +CreatureAI* GetAI_npc_jaina_proudmoore(Creature *_Creature) +{ + hyjalAI* ai = new hyjalAI(_Creature); + + ai->Reset(); + ai->EnterEvadeMode(); + + ai->Spell[0].SpellId = SPELL_BLIZZARD; + ai->Spell[0].Cooldown = 15000 + rand()%20000; + ai->Spell[0].TargetType = TARGETTYPE_RANDOM; + + ai->Spell[1].SpellId = SPELL_PYROBLAST; + ai->Spell[1].Cooldown = 2000 + rand()%7000; + ai->Spell[1].TargetType = TARGETTYPE_RANDOM; + + ai->Spell[2].SpellId = SPELL_SUMMON_ELEMENTALS; + ai->Spell[2].Cooldown = 15000 + rand()%30000; + ai->Spell[2].TargetType = TARGETTYPE_SELF; + + return ai; +} + +bool GossipHello_npc_jaina_proudmoore(Player *player, Creature *_Creature) +{ + hyjalAI* ai = ((hyjalAI*)_Creature->AI()); + if(ai->EventBegun) + return false; + + uint32 RageEncounter = ai->GetInstanceData(DATA_RAGEWINTERCHILLEVENT); + uint32 AnetheronEncounter = ai->GetInstanceData(DATA_ANETHERONEVENT); + if(RageEncounter == NOT_STARTED) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_BEGIN_ALLY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + else if(RageEncounter == DONE && AnetheronEncounter == NOT_STARTED) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_ANETHERON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + else if(RageEncounter == DONE && AnetheronEncounter == DONE) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_RETREAT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + + if(player->isGameMaster()) + player->ADD_GOSSIP_ITEM(2, "[GM] Toggle Debug Timers", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + + player->SEND_GOSSIP_MENU(907, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_jaina_proudmoore(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + player->PlayerTalkClass->GetGossipMenu(); + hyjalAI* ai = ((hyjalAI*)_Creature->AI()); + switch(action) + { + case GOSSIP_ACTION_INFO_DEF + 1: + ai->StartEvent(player); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + ai->FirstBossDead = true; + ai->WaveCount = 9; + ai->StartEvent(player); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + ai->Retreat(); + break; + case GOSSIP_ACTION_INFO_DEF + 4: + ai->Debug = !ai->Debug; + outstring_log("SD2 : HyjalAI - Debug mode has been toggled"); + break; + } + + return true; +} + +CreatureAI* GetAI_npc_thrall(Creature *_Creature) +{ + hyjalAI* ai = new hyjalAI(_Creature); + + ai->Reset(); + ai->EnterEvadeMode(); + + ai->Spell[0].SpellId = SPELL_CHAIN_LIGHTNING; + ai->Spell[0].Cooldown = 2000 + rand()%5000; + ai->Spell[0].TargetType = TARGETTYPE_VICTIM; + + ai->Spell[1].SpellId = SPELL_SUMMON_DIRE_WOLF; + ai->Spell[1].Cooldown = 6000 + rand()%35000; + ai->Spell[1].TargetType = TARGETTYPE_RANDOM; + + return ai; +} + +bool GossipHello_npc_thrall(Player *player, Creature *_Creature) +{ + hyjalAI* ai = ((hyjalAI*)_Creature->AI()); + uint32 AnetheronEvent = ai->GetInstanceData(DATA_ANETHERONEVENT); + if(AnetheronEvent >= DONE && !ai->EventBegun) // Only let them start the Horde phase if Anetheron is dead. + { + uint32 KazrogalEvent = ai->GetInstanceData(DATA_KAZROGALEVENT); + uint32 AzgalorEvent = ai->GetInstanceData(DATA_AZGALOREVENT); + if(KazrogalEvent == NOT_STARTED) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_BEGIN_HORDE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + else if(KazrogalEvent == DONE && AzgalorEvent == NOT_STARTED) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_AZGALOR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + else if(AzgalorEvent == DONE) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_RETREAT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + } + + if(player->isGameMaster()) + player->ADD_GOSSIP_ITEM(2, "[GM] Toggle Debug Timers", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + + player->SEND_GOSSIP_MENU(907, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_thrall(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + player->PlayerTalkClass->GetGossipMenu(); + hyjalAI* ai = ((hyjalAI*)_Creature->AI()); + switch(action) + { + case GOSSIP_ACTION_INFO_DEF + 1: + ai->StartEvent(player); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + ai->FirstBossDead = true; + ai->WaveCount = 9; + ai->StartEvent(player); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + ai->Retreat(); + break; + case GOSSIP_ACTION_INFO_DEF + 4: + ai->Debug = !ai->Debug; + outstring_log("SD2 : HyjalAI - Debug mode has been toggled"); + break; + } + + return true; +} + +bool GossipHello_npc_tyrande_whisperwind(Player* player, Creature* _Creature) +{ + player->ADD_GOSSIP_ITEM(1, "Aid us in defending Nordrassil", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + player->SEND_GOSSIP_MENU(907, _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_tyrande_whisperwind(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if(action == GOSSIP_ACTION_TRADE) + player->SEND_VENDORLIST( _Creature->GetGUID() ); + + return true; +} + +void AddSC_hyjal() +{ + Script *newscript; + + newscript = new Script; + newscript->Name = "npc_jaina_proudmoore"; + newscript->GetAI = GetAI_npc_jaina_proudmoore; + newscript->pGossipHello = &GossipHello_npc_jaina_proudmoore; + newscript->pGossipSelect = &GossipSelect_npc_jaina_proudmoore; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "npc_thrall"; + newscript->GetAI = GetAI_npc_thrall; + newscript->pGossipHello = &GossipHello_npc_thrall; + newscript->pGossipSelect = &GossipSelect_npc_thrall; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "npc_tyrande_whisperwind"; + newscript->pGossipHello = &GossipHello_npc_tyrande_whisperwind; + newscript->pGossipSelect = &GossipSelect_npc_tyrande_whisperwind; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjalAI.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjalAI.cpp new file mode 100644 index 00000000000..0d18f0d9244 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjalAI.cpp @@ -0,0 +1,458 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: HyjalAI +SD%Complete: 99 +SDComment: World Packet workaround for World States +SDCategory: Caverns of Time, Mount Hyjal +EndScriptData */ + +#include "precompiled.h" +#include "hyjalAI.h" +#include "WorldPacket.h" + +float AllianceBase[4][3]= // Locations for summoning waves in Alliance base +{ + {4979.010, -1709.134, 1339.674}, + {4969.123, -1705.904, 1341.363}, + {4970.260, -1698.546, 1341.200}, + {4975.262, -1698.239, 1341.427} +}; + +float HordeBase[4][3]= // Locations for summoning waves in Horde base +{ + {5554.399, -2581.419, 1480.820}, + {5538.996, -2577.742, 1479.790}, + {5565.642, -2565.666, 1481.635}, + {5547.218, -2574.589, 1479.194} +}; + +float AttackArea[2][3]= // used to inform the wave where to move and attack to +{ + { // Alliance + 5042.9189, -1776.2562, 1323.0621 + }, + { // Horde + 5510.4815, -2676.7112, 1480.4314 + } +}; + +hyjalAI::hyjalAI(Creature *c) : ScriptedAI(c) +{ + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); +} + +void hyjalAI::Reset() +{ + /** GUIDs **/ + PlayerGUID = 0; + BossGUID[0] = 0; + BossGUID[1] = 0; + + /** Timers **/ + NextWaveTimer = 10000; + CheckTimer = 0; + RetreatTimer = 1000; + + /** Misc **/ + WaveCount = 0; + + /** Set faction properly based on creature entry**/ + switch(m_creature->GetEntry()) + { + case 17772: + Faction = 0; + DoCast(m_creature, SPELL_BRILLIANCE_AURA, true); + break; + + case 17852: Faction = 1; break; + } + + /** Bools **/ + EventBegun = false; + FirstBossDead = false; + SecondBossDead = false; + Summon = false; + bRetreat = false; + + /** Flags **/ + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + /** Initialize spells **/ + memset(Spell, 0, sizeof(Spell)); + + /** Reset World States **/ + UpdateWorldState(WORLDSTATE_WAVES, 0); + UpdateWorldState(WORLDSTATE_ENEMY, 0); + UpdateWorldState(WORLDSTATE_ENEMYCOUNT, 0); + + /** Reset Instance Data for trash count **/ + if(pInstance) + pInstance->SetData(DATA_RESET_TRASH_COUNT, 0); + else error_log(ERROR_INST_DATA); + + /*** Visibility ***/ + m_creature->SetVisibility(VISIBILITY_ON); + + /** If Jaina evades, reset the visibility of all other creatures in the grid. **/ + if(CreatureList.empty()) + return; + + for(std::list::iterator itr = CreatureList.begin(); itr != CreatureList.end(); ++itr) + if(Creature* cr = ((Creature*)Unit::GetUnit(*m_creature, *itr))) + cr->SetVisibility(VISIBILITY_ON); + + CreatureList.clear(); +} + +void hyjalAI::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(); + + InCombat = false; +} + +void hyjalAI::Aggro(Unit *who) +{ + for(uint8 i = 0; i < 2; ++i) + if(Spell[i].Cooldown) + SpellTimer[i] = Spell[i].Cooldown; + + Talk(ATTACKED); +} + +void hyjalAI::SummonCreature(uint32 entry, float Base[4][3]) +{ + uint32 random = rand()%4; + float SpawnLoc[3]; + float AttackLoc[3]; + + for(uint8 i = 0; i < 3; ++i) + { + SpawnLoc[i] = Base[random][i]; + AttackLoc[i] = AttackArea[Faction][i]; + } + + Creature* pCreature = m_creature->SummonCreature(entry, SpawnLoc[0], SpawnLoc[1], SpawnLoc[2], 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000); + if(pCreature) + { + ++EnemyCount; // Increment Enemy Count to be used in World States and instance script + + pCreature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + pCreature->GetMotionMaster()->MovePoint(0, AttackLoc[0],AttackLoc[1],AttackLoc[2]); + pCreature->AddThreat(m_creature, 1.0f); + DoZoneInCombat(pCreature); + + // Check if creature is a boss. + if(pCreature->GetCreatureInfo()->rank == 3) + { + if(!FirstBossDead) BossGUID[0] = pCreature->GetGUID(); + else BossGUID[1] = pCreature->GetGUID(); + CheckTimer = 5000; + } + } +} + +void hyjalAI::SummonNextWave(Wave wave[18], uint32 Count, float Base[4][3]) +{ + if(rand()%4 == 0) // 1 in 4 chance we give a rally yell. Not sure if the chance is Blizzlike. + Talk(RALLY); + + if(!pInstance) + { + error_log(ERROR_INST_DATA); + return; + } + + EnemyCount = pInstance->GetData(DATA_TRASH); + for(uint8 i = 0; i < 18; ++i) + { + if(wave[Count].Mob[i]) + SummonCreature(wave[Count].Mob[i], Base); + } + + if(!wave[Count].IsBoss) + { + uint32 stateValue = Count+1; + if(FirstBossDead) + stateValue -= 9; // Subtract 9 from it to give the proper wave number if we are greater than 8 + UpdateWorldState(WORLDSTATE_WAVES, stateValue); // Set world state to our current wave number + UpdateWorldState(WORLDSTATE_ENEMY, 1); + //UpdateWorldState(WORLDSTATE_ENEMYCOUNT, EnemyCount); // Let Instance Script handle this + pInstance->SetData(DATA_TRASH, EnemyCount); + if(!Debug) + NextWaveTimer = wave[Count].WaveTimer; + else + { + NextWaveTimer = 15000; + DoTextEmote(": Debug Mode is enabled. Next Wave in 15 seconds", NULL); + } + } + else + { + UpdateWorldState(WORLDSTATE_WAVES, 0); // Set world state for waves to 0 to disable it. + UpdateWorldState(WORLDSTATE_ENEMYCOUNT, 1); // Set World State for enemies invading to 1. + Summon = false; + } + CheckTimer = 5000; +} + +void hyjalAI::StartEvent(Player* player) +{ + if(!player) + return; + + Talk(BEGIN); + + EventBegun = true; + Summon = true; + + NextWaveTimer = 15000; + CheckTimer = 5000; + PlayerGUID = player->GetGUID(); + + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + UpdateWorldState(WORLDSTATE_WAVES, 0); + UpdateWorldState(WORLDSTATE_ENEMY, 0); + UpdateWorldState(WORLDSTATE_ENEMYCOUNT, 0); +} + +uint32 hyjalAI::GetInstanceData(uint32 Event) +{ + if(pInstance) + return pInstance->GetData(Event); + else error_log(ERROR_INST_DATA); + + return 0; +} + +void hyjalAI::Talk(uint32 id) +{ + std::list index; + for(uint8 i = 0; i < 10; i++) + { + if(Faction == 0) // Alliance + { + if(JainaQuotes[i].id == id) + index.push_back(i); + } + else if(Faction == 1) // Horde + { + if(ThrallQuotes[i].id == id) + index.push_back(i); + } + } + + if(index.empty()) + return; // No quotes found, no use to continue + + uint8 ind = *(index.begin()) + rand()%index.size(); + + char* Yell = NULL; + uint32 Sound = 0; + if(Faction == 0) // Alliance + { + Yell = JainaQuotes[ind].text; + Sound = JainaQuotes[ind].sound; + } + else if(Faction == 1) // Horde + { + Yell = ThrallQuotes[ind].text; + Sound = ThrallQuotes[ind].sound; + } + + if(Yell) + DoYell(Yell, LANG_UNIVERSAL, NULL); + if(Sound) + DoPlaySoundToSet(m_creature, Sound); +} + +// Slight workaround for now +void hyjalAI::UpdateWorldState(uint32 field, uint32 value) +{ + Map * map = m_creature->GetMap(); + if(!map->IsDungeon()) return; + + WorldPacket data(SMSG_UPDATE_WORLD_STATE, 8); + + data << field; + data << value; + + ((InstanceMap*)map)->SendToPlayers(&data); + + // TODO: Uncomment and remove everything above this line only when the core patch for this is accepted + //m_creature->GetMap()->UpdateWorldState(field, value); +} + +void hyjalAI::Retreat() +{ + CellPair pair(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY())); + Cell cell(pair); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + // First get all creatures. + std::list creatures; + AllFriendlyCreaturesInGrid creature_check(m_creature); + MaNGOS::CreatureListSearcher creature_searcher(creatures, creature_check); + TypeContainerVisitor + , + GridTypeMapContainer> creature_visitor(creature_searcher); + + // Then get all Ancient Gem Veins. NOTE: Grid Search will only be able to find those in the grid. + std::list goList; + AllGameObjectsWithEntryInGrid go_check(185557); + MaNGOS::GameObjectListSearcher go_search(goList, go_check); + TypeContainerVisitor + , GridTypeMapContainer> go_visit(go_search); + + CellLock cell_lock(cell, pair); + // Get Creatures + cell_lock->Visit(cell_lock, creature_visitor, *(m_creature->GetMap())); + // Get GOs + cell_lock->Visit(cell_lock, go_visit, *(m_creature->GetMap())); + + CreatureList.clear(); + if(!creatures.empty()) + { + for(std::list::iterator itr = creatures.begin(); itr != creatures.end(); ++itr) + { + (*itr)->CastSpell(*itr, SPELL_TELEPORT_VISUAL, true); + CreatureList.push_back((*itr)->GetGUID()); + } + + DoCast(m_creature, SPELL_TELEPORT_VISUAL); + bRetreat = true; + RetreatTimer = 1000; + } + + if(!goList.empty()) + { + for(std::list::iterator itr = goList.begin(); itr != goList.end(); ++itr) + (*itr)->SetRespawnTime(5000); + } +} + +void hyjalAI::UpdateAI(const uint32 diff) +{ + if(bRetreat) + if(RetreatTimer < diff) + { + bRetreat = false; + if(CreatureList.empty()) + return; + + for(std::list::iterator itr = CreatureList.begin(); itr != CreatureList.end(); ++itr) + if(Unit* pUnit = Unit::GetUnit(*m_creature, *itr)) + pUnit->SetVisibility(VISIBILITY_OFF); + + m_creature->SetVisibility(VISIBILITY_OFF); + }else RetreatTimer -= diff; + + if(!EventBegun) + return; + + if(Summon) + { + if(pInstance && EnemyCount) + { + EnemyCount = pInstance->GetData(DATA_TRASH); + if(!EnemyCount) + NextWaveTimer = 5000; + } + + if(NextWaveTimer < diff) + { + if(Faction == 0) + SummonNextWave(AllianceWaves, WaveCount, AllianceBase); + else if(Faction == 1) + SummonNextWave(HordeWaves, WaveCount, HordeBase); + ++WaveCount; + }else NextWaveTimer -= diff; + } + + if(CheckTimer < diff) + { + for(uint8 i = 0; i < 2; ++i) + { + if(BossGUID[i]) + { + Unit* pUnit = Unit::GetUnit((*m_creature), BossGUID[i]); + if(pUnit && (!pUnit->isAlive())) + { + if(BossGUID[i] == BossGUID[0]) + { + Talk(INCOMING); + FirstBossDead = true; + } + else if(BossGUID[i] == BossGUID[1]) + { + Talk(SUCCESS); + SecondBossDead = true; + } + EventBegun = false; + CheckTimer = 0; + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + BossGUID[i] = 0; + UpdateWorldState(WORLDSTATE_ENEMY, 0); // Reset world state for enemies to disable it + } + } + } + CheckTimer = 5000; + }else CheckTimer -= diff; + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + for(uint8 i = 0; i < 3; ++i) + { + if(Spell[i].SpellId) + { + if(SpellTimer[i] < diff) + { + if(m_creature->IsNonMeleeSpellCasted(false)) + m_creature->InterruptNonMeleeSpells(false); + + Unit* target = NULL; + + switch(Spell[i].TargetType) + { + case TARGETTYPE_SELF: target = m_creature; break; + case TARGETTYPE_RANDOM: target = SelectUnit(SELECT_TARGET_RANDOM, 0); break; + case TARGETTYPE_VICTIM: target = m_creature->getVictim(); break; + } + + if(target && target->isAlive()) + { + DoCast(target, Spell[i].SpellId); + SpellTimer[i] = Spell[i].Cooldown; + } + }else SpellTimer[i] -= diff; + } + } + + DoMeleeAttackIfReady(); +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjalAI.h b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjalAI.h new file mode 100644 index 00000000000..46be6a83879 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/hyjalAI.h @@ -0,0 +1,286 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef SC_HYJALAI_H +#define SC_HYJALAI_H + +#include "def_hyjal.h" + +// Trash Mobs summoned in waves +#define NECROMANCER 17899 +#define ABOMINATION 17898 +#define GHOUL 17895 +#define BANSHEE 17905 +#define CRYPT_FIEND 17897 +#define GARGOYLE 17906 +#define FROST_WYRM 17907 +#define GIANT_INFERNAL 17908 +#define FEL_STALKER 17916 + +// Bosses summoned after every 8 waves +#define RAGE_WINTERCHILL 17767 +#define ANETHERON 17808 +#define KAZROGAL 17888 +#define AZGALOR 17842 +#define ARCHIMONDE 17968 + +#define SPELL_TELEPORT_VISUAL 41232 + +#define WORLDSTATE_WAVES 2842 +#define WORLDSTATE_ENEMY 2453 +#define WORLDSTATE_ENEMYCOUNT 2454 + +/*** Spells for Jaina ***/ +#define SPELL_BRILLIANCE_AURA 31260 // The database must handle this spell via creature_addon +#define SPELL_BLIZZARD 31266 +#define SPELL_PYROBLAST 31263 +#define SPELL_SUMMON_ELEMENTALS 31264 + +/** Thrall spells **/ +#define SPELL_CHAIN_LIGHTNING 31330 +#define SPELL_SUMMON_DIRE_WOLF 31331 + +struct Wave +{ + uint32 Mob[18]; // Stores Creature Entries to be summoned in Waves + uint32 WaveTimer; // The timer before the next wave is summoned + bool IsBoss; // Simply used to inform the wave summoner that the next wave contains a boss to halt all waves after that +}; + +static Wave AllianceWaves[]= // Waves that will be summoned in the Alliance Base +{ + { // Wave 1 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, 0, 0, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 2 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 3 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 4 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 5 + GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 6 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 7 + GHOUL, GHOUL, GHOUL, GHOUL, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 8 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 180000, false + }, + { // All 8 Waves are summoned, summon Rage Winterchill, next few waves are for Anetheron + RAGE_WINTERCHILL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, true + }, + { // Wave 1 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, 0, 0, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 2 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 3 + GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 4 + GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, BANSHEE, BANSHEE, BANSHEE, BANSHEE, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 5 + CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, NECROMANCER, NECROMANCER, BANSHEE, BANSHEE, BANSHEE, BANSHEE, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 6 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 7 + CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, BANSHEE, BANSHEE, BANSHEE, BANSHEE, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 8 + GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, BANSHEE, BANSHEE, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 180000, false + }, + { // All 8 Waves are summoned, summon Anatheron + ANETHERON, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, true + } +}; + +static Wave HordeWaves[]= // Waves that are summoned in the Horde base +{ + { // Wave 1 + GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, BANSHEE, BANSHEE, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 2 + GHOUL, GHOUL, GHOUL, GHOUL, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, 0, 0, 0, 0, 120000, false + }, + { // Wave 3 + GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 120000, false + }, + { // Wave 4 + CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, NECROMANCER, NECROMANCER , 0, 0, 0, 0, 120000, false + }, + { // Wave 5 + GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 6 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, NECROMANCER, FROST_WYRM, 0, 0, 120000, false + }, + { // Wave 7 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, NECROMANCER, FROST_WYRM, 0, 0, 120000, false + }, + { // Wave 8 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, BANSHEE, BANSHEE, 180000, false + }, + { // All 8 Waves are summoned, summon Kaz'Rogal, next few waves are for Azgalor + KAZROGAL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, true + }, + { // Wave 1 + ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 2 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, FROST_WYRM, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, GARGOYLE, 0, 0, 0, 0, 120000, false + }, + { // Wave 3 + GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, 0, 0, 0, 0, 120000, false + }, + { // Wave 4 + GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, 0, 0, 0, 0, 120000, false + }, + { // Wave 5 + FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, FEL_STALKER, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 120000, false + }, + { // Wave 6 + NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, NECROMANCER, BANSHEE, BANSHEE, BANSHEE, BANSHEE, BANSHEE, BANSHEE, 0, 0, 0, 0, 120000, false + }, + { // Wave 7 + GHOUL, GHOUL, CRYPT_FIEND, CRYPT_FIEND, FEL_STALKER, FEL_STALKER, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, GIANT_INFERNAL, 0, 0, 0, 0, 0, 0, 120000, false + }, + { // Wave 8 + CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, CRYPT_FIEND, FEL_STALKER, FEL_STALKER, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, BANSHEE, BANSHEE, BANSHEE, BANSHEE, NECROMANCER, NECROMANCER, 0, 0, 180000, false + }, + { // All 8 Waves are summoned, summon Azgalor + AZGALOR, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, true + } +}; + +enum TargetType // Used in the spell cast system for the AI +{ + TARGETTYPE_SELF = 0, + TARGETTYPE_RANDOM = 1, + TARGETTYPE_VICTIM = 2, +}; + +struct Yells +{ + uint32 id; // Used to determine the type of yell (attack, rally, etc) + char* text; // The text to be yelled + uint32 sound; // Sound that corresponds to the text +}; + +enum YellId +{ + ATTACKED = 0, // Used when attacked and set in combat + BEGIN = 1, // Used when the event is begun + INCOMING = 2, // Used to warn the raid that another wave phase is coming + RALLY = 3, // Used to rally the raid and warn that the next wave has been summoned + FAILURE = 4, // Used when raid has failed (unsure where to place) + SUCCESS = 5, // Used when the raid has sucessfully defeated a wave phase + DEATH = 6, // Used on death +}; + +static Yells JainaQuotes[]= +{ + {ATTACKED, "I'm in jeopardy, help me if you can!", 11007}, + {ATTACKED, "They've broken through!", 11049}, + {INCOMING, "Stay alert! Another wave approaches.", 11008}, + {BEGIN, "Hold them back as long as possible", 11050}, + {RALLY, "Don't give up! We must prevail!", 11006}, + {RALLY, "We must hold strong!", 11051}, + {FAILURE, "We are lost. Fall back!", 11009}, + {SUCCESS, "We have won valuable time. Now we must pull back!", 11011}, + {DEATH, "I did... my best.", 11010}, +}; + +static Yells ThrallQuotes[]= +{ + {ATTACKED, "I will lie down for no one!", 11031}, + {ATTACKED, "Bring the fight to me and pay with your lives!", 11061}, + {INCOMING, "Make ready for another wave! LOK-TAR OGAR!", 11032}, + {BEGIN, "Do not give an inch of ground!", 11060}, + {RALLY, "Hold them back! Do not falter!", 11030}, + {RALLY, "Victory or death!", 11059}, + {RALLY, "Do not give an inch of ground!", 11060}, + {FAILURE, "It is over. Withdraw! We have failed.", 11033}, + {SUCCESS, "We have played our part and done well. It is up to the others now.", 11035}, + {DEATH, "Uraaa...", 11034}, +}; + +struct MANGOS_DLL_DECL hyjalAI : public ScriptedAI +{ + hyjalAI(Creature *c); + + void Reset(); // Generically used to reset our variables. Do *not* call in EnterEvadeMode as this may make problems if the raid is still in combat + + void EnterEvadeMode(); // Send creature back to spawn location and evade. + + void Aggro(Unit *who); // Used to reset cooldowns for our spells and to inform the raid that we're under attack + + void UpdateAI(const uint32 diff); // Called to summon waves, check for boss deaths and to cast our spells. + + void JustDied(Unit* killer) // Called on death, informs the raid that they have failed. + { + Talk(DEATH); + } + + void SetFaction(uint32 _faction) // Set the faction to either Alliance or Horde in Hyjal + { + Faction = _faction; + } + + void Retreat(); // "Teleport" (teleport visual + set invisible) all friendly creatures away from the base. + + void SummonCreature(uint32 entry, float Base[4][3]); // Summons a creature for that wave in that base + + // Summons the next wave, calls SummonCreature + void SummonNextWave(Wave wave[18], uint32 Count, float Base[4][3]); + + void StartEvent(Player* player); // Begins the event by gossip click + + uint32 GetInstanceData(uint32 Event); // Gets instance data for this instance, used to check if raid has gotten past a certain point and can access the next phase + + void Talk(uint32 id); // Searches for the appropriate yell and sound and uses it to inform the raid of various things + + void UpdateWorldState(uint32 field, uint32 value); // NYI: Requires core support. Updates the world state counter at the top of the UI. + public: + ScriptedInstance* pInstance; + + uint64 PlayerGUID; + uint64 BossGUID[2]; + + uint32 NextWaveTimer; + uint32 WaveCount; + uint32 CheckTimer; + uint32 Faction; + uint32 EnemyCount; + uint32 RetreatTimer; + + bool EventBegun; + bool FirstBossDead; + bool SecondBossDead; + bool Summon; + bool bRetreat; + bool Debug; + + struct Spell + { + uint32 SpellId; + uint32 Cooldown; + uint32 TargetType; + }Spell[3]; + + private: + uint32 SpellTimer[3]; + std::list CreatureList; +}; +#endif diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/instance_hyjal.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/instance_hyjal.cpp new file mode 100644 index 00000000000..796069ba81f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/hyjal/instance_hyjal.cpp @@ -0,0 +1,203 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Mount_Hyjal +SD%Complete: 100 +SDComment: Instance Data Scripts and functions to acquire mobs and set encounter status for use in various Hyjal Scripts +SDCategory: Caverns of Time, Mount Hyjal +EndScriptData */ + +#include "precompiled.h" +#include "def_hyjal.h" +#include "WorldPacket.h" + +#define ENCOUNTERS 5 + +/* Battle of Mount Hyjal encounters: +0 - Rage Winterchill event +1 - Anetheron event +2 - Kaz'rogal event +3 - Azgalor event +4 - Archimonde event +*/ + +struct MANGOS_DLL_DECL instance_mount_hyjal : public ScriptedInstance +{ + instance_mount_hyjal(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint64 RageWinterchill; + uint64 Anetheron; + uint64 Kazrogal; + uint64 Azgalor; + uint64 Archimonde; + uint64 JainaProudmoore; + uint64 Thrall; + uint64 TyrandeWhisperwind; + + uint32 Trash; + uint32 Encounters[ENCOUNTERS]; + + void Initialize() + { + RageWinterchill = 0; + Anetheron = 0; + Kazrogal = 0; + Azgalor = 0; + Archimonde = 0; + JainaProudmoore = 0; + Thrall = 0; + TyrandeWhisperwind = 0; + + Trash = 0; + for(uint8 i = 0; i < ENCOUNTERS; ++i) + Encounters[i] = NOT_STARTED; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < ENCOUNTERS; ++i) + if(Encounters[i] == IN_PROGRESS) return true; + + return false; + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case 17767: RageWinterchill = creature->GetGUID(); break; + case 17808: Anetheron = creature->GetGUID(); break; + case 17888: Kazrogal = creature->GetGUID(); break; + case 17842: Azgalor = creature->GetGUID(); break; + case 17968: Archimonde = creature->GetGUID(); break; + case 17772: JainaProudmoore = creature->GetGUID(); break; + case 17852: Thrall = creature->GetGUID(); break; + case 17948: TyrandeWhisperwind = creature->GetGUID(); break; + } + } + + uint64 GetData64(uint32 identifier) + { + switch(identifier) + { + case DATA_RAGEWINTERCHILL: return RageWinterchill; + case DATA_ANETHERON: return Anetheron; + case DATA_KAZROGAL: return Kazrogal; + case DATA_AZGALOR: return Azgalor; + case DATA_ARCHIMONDE: return Archimonde; + case DATA_JAINAPROUDMOORE: return JainaProudmoore; + case DATA_THRALL: return Thrall; + case DATA_TYRANDEWHISPERWIND: return TyrandeWhisperwind; + } + + return 0; + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_RAGEWINTERCHILLEVENT: Encounters[0] = data; break; + case DATA_ANETHERONEVENT: Encounters[1] = data; break; + case DATA_KAZROGALEVENT: Encounters[2] = data; break; + case DATA_AZGALOREVENT: Encounters[3] = data; break; + case DATA_ARCHIMONDEEVENT: Encounters[4] = data; break; + case DATA_RESET_TRASH_COUNT: Trash = 0; break; + + case DATA_TRASH: + if(data) Trash = data; + else Trash--; + UpdateWorldState(2453, data); + break; + } + + if(data == DONE) + SaveToDB(); + } + + uint32 GetData(uint32 type) + { + switch(type) + { + case DATA_RAGEWINTERCHILLEVENT: return Encounters[0]; + case DATA_ANETHERONEVENT: return Encounters[1]; + case DATA_KAZROGALEVENT: return Encounters[2]; + case DATA_AZGALOREVENT: return Encounters[3]; + case DATA_ARCHIMONDEEVENT: return Encounters[4]; + case DATA_TRASH: return Trash; + } + return 0; + } + + void UpdateWorldState(uint32 field, uint32 value) + { + WorldPacket data(SMSG_UPDATE_WORLD_STATE, 8); + data << field; + data << value; + + ((InstanceMap*)instance)->SendToPlayers(&data); + } + + const char* Save() + { + OUT_SAVE_INST_DATA; + std::ostringstream stream; + stream << Encounters[0] << " " << Encounters[1] << " " + << Encounters[2] << " " << Encounters[3] << " " + << Encounters[4]; + char* out = new char[stream.str().length() + 1]; + strcpy(out, stream.str().c_str()); + if(out) + { + OUT_SAVE_INST_DATA_COMPLETE; + return out; + } + + return NULL; + } + + void Load(const char* load) + { + if(!load) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(load); + std::istringstream stream; + stream >> Encounters[1] >> Encounters[2] >> Encounters[3] >> Encounters[4]; + for(uint8 i = 0; i < ENCOUNTERS; ++i) + if(Encounters[i] == IN_PROGRESS) // Do not load an encounter as IN_PROGRESS - reset it instead. + Encounters[i] = NOT_STARTED; + OUT_LOAD_INST_DATA_COMPLETE; + } +}; + +InstanceData* GetInstanceData_instance_mount_hyjal(Map* map) +{ + return new instance_mount_hyjal(map); +} + +void AddSC_instance_mount_hyjal() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_hyjal"; + newscript->GetInstanceData = GetInstanceData_instance_mount_hyjal; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp new file mode 100644 index 00000000000..2dbb79b02a1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp @@ -0,0 +1,177 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Captain_Skarloc +SD%Complete: 75 +SDComment: Missing adds, missing waypoints to move up to Thrall once spawned + speech before enter combat. +SDCategory: Caverns of Time, Old Hillsbrad Foothills +EndScriptData */ + +#include "precompiled.h" +#include "def_old_hillsbrad.h" + +#define HOLY_LIGHT 29562 +#define CLEANSE 39078 +#define HAMMER_OF_JUSTICE 13005 +#define HOLY_SHIELD 31904 +#define DEVOTION_AURA 41452 +#define CONSECRATION 41541 + +#define SAY_ENTER "Thrall! You didn't really think you would escape did you? You and your allies shall answer to Blackmoore - after I've had my fun!" +#define SAY_AGGRO1 "You're a slave. That's all you'll ever be.'" +#define SAY_AGGRO2 "I don't know what Blackmoore sees in you. For my money, you're just another ignorant savage!" +#define SAY_SLAY1 "Thrall will never be free!" +#define SAY_SLAY2 "Did you really think you would leave here alive?" +#define SAY_DEATH "Guards! Urgh..Guards..!'" + +#define SOUND_ENTER 10406 +#define SOUND_AGGRO1 10407 +#define SOUND_AGGRO2 10408 +#define SOUND_SLAY1 10409 +#define SOUND_SLAY2 10410 +#define SOUND_DEATH 10411 + +struct MANGOS_DLL_DECL boss_captain_skarlocAI : public ScriptedAI +{ + boss_captain_skarlocAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 Holy_Light_Timer; + uint32 Cleanse_Timer; + uint32 HammerOfJustice_Timer; + uint32 HolyShield_Timer; + uint32 DevotionAura_Timer; + uint32 Consecration_Timer; + + void Reset() + { + Holy_Light_Timer = 30000; + Cleanse_Timer = 10000; + HammerOfJustice_Timer = 60000; + HolyShield_Timer = 240000; + DevotionAura_Timer = 60000; + Consecration_Timer = 8000; + } + + void Aggro(Unit *who) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_AGGRO1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + break; + } + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + if( pInstance && pInstance->GetData(TYPE_THRALL_EVENT) == IN_PROGRESS ) + pInstance->SetData(TYPE_THRALL_PART1, DONE); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Holy_Light + if (Holy_Light_Timer < diff) + { + DoCast(m_creature, HOLY_LIGHT); + Holy_Light_Timer = 30000; + }else Holy_Light_Timer -= diff; + + //Cleanse + if(Cleanse_Timer < diff) + { + DoCast(m_creature, CLEANSE); + Cleanse_Timer = 10000 ; + } else Cleanse_Timer -= diff; + + //Hammer of Justice + if (HammerOfJustice_Timer < diff) + { + DoCast(m_creature->getVictim(), HAMMER_OF_JUSTICE); + HammerOfJustice_Timer = 60000; + }else HammerOfJustice_Timer -= diff; + + //Holy Shield + if (HolyShield_Timer < diff) + { + DoCast(m_creature,HOLY_SHIELD); + HolyShield_Timer = 240000; + }else HolyShield_Timer -= diff; + + //Devotion_Aura + if (DevotionAura_Timer < diff) + { + DoCast(m_creature,DEVOTION_AURA); + DevotionAura_Timer = 60000; + }else DevotionAura_Timer -= diff; + + //Consecration + if (Consecration_Timer < diff) + { + //DoCast(m_creature->getVictim(),CONSECRATION); + Consecration_Timer = 8000; + }else Consecration_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_captain_skarloc(Creature *_Creature) +{ + return new boss_captain_skarlocAI (_Creature); +} + +void AddSC_boss_captain_skarloc() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_captain_skarloc"; + newscript->GetAI = GetAI_boss_captain_skarloc; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp new file mode 100644 index 00000000000..bbcaec53407 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp @@ -0,0 +1,183 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Epoch_Hunter +SD%Complete: 60 +SDComment: Missing spawns pre-event, missing speech to be coordinated with rest of escort event. +SDCategory: Caverns of Time, Old Hillsbrad Foothills +EndScriptData */ + +#include "precompiled.h" +#include "def_old_hillsbrad.h" + +#define SAY_ENTER1 "Thrall! Come outside and face your fate!" +#define SOUND_ENTER1 10418 +#define SAY_ENTER2 "Taretha's life hangs in the balance. Surely you care for her. Surely you wish to save her..." +#define SOUND_ENTER2 10419 +#define SAY_ENTER3 "Ah, there you are. I had hoped to accomplish this with a bit of subtlety, but I suppose direct confrontation was inevitable. Your future, Thrall, must not come to pass and so...you and your troublesome friends must die!" +#define SOUND_ENTER3 10420 + +#define SAY_AGGRO1 "Enough! I will erase your very existence!" +#define SOUND_AGGRO1 10421 +#define SAY_AGGRO2 "You cannot fight fate!" +#define SOUND_AGGRO2 10422 + +#define SAY_SLAY1 "You are...irrelevant." +#define SOUND_SLAY1 10425 +#define SAY_SLAY2 "Thrall will remain a slave. Taretha will die. You have failed." +#define SOUND_SLAY2 10426 + +#define SAY_BREATH1 "Not so fast!" +#define SOUND_BREATH1 10423 +#define SAY_BREATH2 "Struggle as much as you like!" +#define SOUND_BREATH2 10424 + +#define SAY_DEATH "No!...The master... will not... be pleased." +#define SOUND_DEATH 10427 + +#define SPELL_SAND_BREATH 31914 +#define SPELL_IMPENDING_DEATH 31916 +#define SPELL_MAGIC_DISRUPTION_AURA 33834 +#define SPELL_WING_BUFFET 31475 + +struct MANGOS_DLL_DECL boss_epoch_hunterAI : public ScriptedAI +{ + boss_epoch_hunterAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 SandBreath_Timer; + uint32 ImpendingDeath_Timer; + uint32 WingBuffet_Timer; + uint32 Mda_Timer; + + void Reset() + { + SandBreath_Timer = 25000; + ImpendingDeath_Timer = 30000; + WingBuffet_Timer = 35000; + Mda_Timer = 40000; + } + + void Aggro(Unit *who) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_AGGRO1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + break; + } + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if( pInstance && pInstance->GetData(TYPE_THRALL_EVENT) == IN_PROGRESS ) + pInstance->SetData(TYPE_THRALL_PART4, DONE); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Sand Breath + if( SandBreath_Timer < diff ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(false); + + DoCast(m_creature->getVictim(),SPELL_SAND_BREATH); + + switch(rand()%2) + { + case 0: + DoYell(SAY_BREATH1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_BREATH1); + break; + case 1: + DoYell(SAY_BREATH2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_BREATH2); + break; + } + + SandBreath_Timer = 25000+rand()%5000; + }else SandBreath_Timer -= diff; + + if( ImpendingDeath_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_IMPENDING_DEATH); + ImpendingDeath_Timer = 30000+rand()%5000; + }else ImpendingDeath_Timer -= diff; + + if( WingBuffet_Timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_WING_BUFFET); + WingBuffet_Timer = 25000+rand()%10000; + }else WingBuffet_Timer -= diff; + + if( Mda_Timer < diff ) + { + DoCast(m_creature,SPELL_MAGIC_DISRUPTION_AURA); + Mda_Timer = 15000; + }else Mda_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_epoch_hunter(Creature *_Creature) +{ + return new boss_epoch_hunterAI (_Creature); +} + +void AddSC_boss_epoch_hunter() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_epoch_hunter"; + newscript->GetAI = GetAI_boss_epoch_hunter; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp new file mode 100644 index 00000000000..5a6ccb7dc97 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp @@ -0,0 +1,246 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Luetenant_Drake +SD%Complete: 70 +SDComment: Missing proper code for patrolling area after being spawned. Also script for GO (barrels)(missing mangos support) +SDCategory: Caverns of Time, Old Hillsbrad Foothills +EndScriptData */ + +#include "precompiled.h" +#include "def_old_hillsbrad.h" +#include "../../../npc/npc_escortAI.h" + +/*###### +## go_barrel_old_hillsbrad +######*/ + +#define QUEST_ENTRY_DIVERSION 10283 +#define LODGE_QUEST_TRIGGER 20155 + +bool GOHello_go_barrel_old_hillsbrad(Player *player, GameObject* _GO) +{ + ScriptedInstance* pInstance = ((ScriptedInstance*)_GO->GetInstanceData()); + + if( pInstance ) + { + if( pInstance->GetData(TYPE_BARREL_DIVERSION) != DONE ) + { + pInstance->SetData(TYPE_BARREL_DIVERSION, IN_PROGRESS); + } + else if( pInstance->GetData(TYPE_BARREL_DIVERSION) == DONE ) + { + player->SummonCreature(17848,2128.43,71.01,64.42,1.74,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,1200000); + if( player->GetQuestStatus(QUEST_ENTRY_DIVERSION) == QUEST_STATUS_INCOMPLETE ) + player->KilledMonster(LODGE_QUEST_TRIGGER,0); + } + } + return false; +} + +/*###### +## boss_lieutenant_drake +######*/ + +#define WHIRLWIND 40236 +#define FEAR 33789 +#define MORTAL_STRIKE 40220 +#define EXPLODIG_SHOUT 33792 + +#define SAY_ENTER1 "You there, fetch water quickly!" +#define SAY_ENTER2 "Get these flames out before they spread to the rest of the keep!" +#define SAY_ENTER3 "Hurry, damn you!" +#define SAY_AGGRO "I know what you're up to, and I mean to put an end to it, permanently!" +#define SAY_SLAY1 "No more middling for you." +#define SAY_SLAY2 "You will not interfere!" +#define SAY_MORTAL "Time to bleed!" +#define SAY_SHOUT "Run, you blasted cowards!" +#define SAY_DEATH "Thrall... must not... go free." + +#define SOUND_ENTER 10428 +#define SOUND_AGGRO 10429 +#define SOUND_SLAY1 10432 +#define SOUND_SLAY2 10433 +#define SOUND_MORTAL 10430 +#define SOUND_SHOUT 10431 +#define SOUND_DEATH 10434 + +struct Location +{ + uint32 wpId; + float x; + float y; + float z; +}; + +static Location DrakeWP[]= +{ + {0, 2125.84, 88.2535, 54.8830}, + {1, 2111.01, 93.8022, 52.6356}, + {2, 2106.70, 114.753, 53.1965}, + {3, 2107.76, 138.746, 52.5109}, + {4, 2114.83, 160.142, 52.4738}, + {5, 2125.24, 178.909, 52.7283}, + {6, 2151.02, 208.901, 53.1551}, + {7, 2177.00, 233.069, 52.4409}, + {8, 2190.71, 227.831, 53.2742}, + {9, 2178.14, 214.219, 53.0779}, + {10, 2154.99, 202.795, 52.6446}, + {11, 2132.00, 191.834, 52.5709}, + {12, 2117.59, 166.708, 52.7686}, + {13, 2093.61, 139.441, 52.7616}, + {14, 2086.29, 104.950, 52.9246}, + {15, 2094.23, 81.2788, 52.6946}, + {16, 2108.70, 85.3075, 53.3294}, + {17, 2125.50, 88.9481, 54.7953}, + {18, 2128.20, 70.9763, 64.4221} +}; + +struct MANGOS_DLL_DECL boss_lieutenant_drakeAI : public ScriptedAI +{ + boss_lieutenant_drakeAI(Creature *c) : ScriptedAI(c) {Reset();} + + bool CanPatrol; + uint32 wpId; + + uint32 Whirlwind_Timer; + uint32 Fear_Timer; + uint32 MortalStrike_Timer; + uint32 ExplodingShout_Timer; + + void Reset() + { + CanPatrol = true; + wpId = 0; + + Whirlwind_Timer = 20000; + Fear_Timer = 30000; + MortalStrike_Timer = 45000; + ExplodingShout_Timer = 25000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void UpdateAI(const uint32 diff) + { + //TODO: make this work + if( CanPatrol && wpId == 0 ) + { + m_creature->GetMotionMaster()->MovePoint(DrakeWP[0].wpId, DrakeWP[0].x, DrakeWP[0].y, DrakeWP[0].z); + wpId++; + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Whirlwind + if (Whirlwind_Timer < diff) + { + DoCast(m_creature->getVictim(), WHIRLWIND); + + Whirlwind_Timer = 20000+rand()%5000; + }else Whirlwind_Timer -= diff; + + //Fear + if (Fear_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (target) + DoCast(target, FEAR); + + Fear_Timer = 30000+rand()%10000; + }else Fear_Timer -= diff; + + //Mortal Strike + if (MortalStrike_Timer < diff) + { + DoYell(SAY_MORTAL, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_MORTAL); + + DoCast(m_creature->getVictim(), MORTAL_STRIKE); + + MortalStrike_Timer = 45000+rand()%5000; + }else MortalStrike_Timer -= diff; + + /* + //This only enabled on heroic? + //Exploding Shout + if (m_creature->IsHeroicCreature()) + { + if (ExplodingShout_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + DoYell(SAY_SHOUT, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SHOUT); + DoCast(target,EXPLODING_SHOUT); + ExplodingShout_Timer = 25000+rand()%5000; + }else ExplodingShout_Timer -= diff; + } + */ + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_lieutenant_drake(Creature *_Creature) +{ + return new boss_lieutenant_drakeAI (_Creature); +} + +void AddSC_boss_lieutenant_drake() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="go_barrel_old_hillsbrad"; + newscript->pGOHello = GOHello_go_barrel_old_hillsbrad; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_lieutenant_drake"; + newscript->GetAI = GetAI_boss_lieutenant_drake; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/def_old_hillsbrad.h b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/def_old_hillsbrad.h new file mode 100644 index 00000000000..8a230919bbc --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/def_old_hillsbrad.h @@ -0,0 +1,16 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_OLD_HILLSBRAD_H +#define DEF_OLD_HILLSBRAD_H + +#define TYPE_BARREL_DIVERSION 1 +#define TYPE_THRALL_EVENT 2 +#define TYPE_THRALL_PART1 3 +#define TYPE_THRALL_PART2 4 +#define TYPE_THRALL_PART3 5 +#define TYPE_THRALL_PART4 6 +#define DATA_THRALL 7 +#define DATA_TARETHA 8 +#endif diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp new file mode 100644 index 00000000000..57d78f5d98f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp @@ -0,0 +1,178 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Old_Hillsbrad +SD%Complete: 60 +SDComment: If thrall escort fail, all parts will reset. In future, save sub-parts and continue from last known. +SDCategory: Caverns of Time, Old Hillsbrad Foothills +EndScriptData */ + +/* +-- UPDATE `gameobject_template` SET `ScriptName`='go_barrel_old_hillsbrad' WHERE `entry`=182589; +*/ + +#include "precompiled.h" +#include "def_old_hillsbrad.h" + +#define ENCOUNTERS 6 + +#define THRALL_ENTRY 17876 +#define TARETHA_ENTRY 18887 + +struct MANGOS_DLL_DECL instance_old_hillsbrad : public ScriptedInstance +{ + instance_old_hillsbrad(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint32 Encounter[ENCOUNTERS]; + uint32 mBarrelCount; + uint32 mThrallEventCount; + + uint64 ThrallGUID; + uint64 TarethaGUID; + + void Initialize() + { + mBarrelCount = 0; + mThrallEventCount = 0; + ThrallGUID = 0; + TarethaGUID = 0; + + for(uint8 i = 0; i < ENCOUNTERS; i++) + Encounter[i] = NOT_STARTED; + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case THRALL_ENTRY: + ThrallGUID = creature->GetGUID(); + break; + case TARETHA_ENTRY: + TarethaGUID = creature->GetGUID(); + break; + } + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case TYPE_BARREL_DIVERSION: + { + if( mBarrelCount < 5 ) + { + mBarrelCount++; + debug_log("SD2: go_barrel_old_hillsbrad count %u",mBarrelCount); + } + else if( mBarrelCount >= 5 ) + Encounter[0] = DONE; + break; + } + case TYPE_THRALL_EVENT: + { + if( data == FAIL ) + { + if( mThrallEventCount <= 20 ) + { + mThrallEventCount++; + Encounter[1] = NOT_STARTED; + debug_log("SD2: Thrall event failed %u times. Resetting all sub-events.",mThrallEventCount); + Encounter[2] = NOT_STARTED; + Encounter[3] = NOT_STARTED; + Encounter[4] = NOT_STARTED; + Encounter[5] = NOT_STARTED; + } + else if( mThrallEventCount > 20 ) + { + Encounter[1] = data; + Encounter[2] = data; + Encounter[3] = data; + Encounter[4] = data; + Encounter[5] = data; + debug_log("SD2: Thrall event failed %u times. Reset instance required.",mThrallEventCount); + } + } + else + Encounter[1] = data; + debug_log("SD2: Thrall escort event adjusted to data %u.",data); + break; + } + case TYPE_THRALL_PART1: + Encounter[2] = data; + debug_log("SD2: Thrall event part I adjusted to data %u.",data); + break; + case TYPE_THRALL_PART2: + Encounter[3] = data; + debug_log("SD2: Thrall event part II adjusted to data %u.",data); + break; + case TYPE_THRALL_PART3: + Encounter[4] = data; + debug_log("SD2: Thrall event part III adjusted to data %u.",data); + break; + case TYPE_THRALL_PART4: + Encounter[5] = data; + debug_log("SD2: Thrall event part IV adjusted to data %u.",data); + break; + } + } + + uint32 GetData(uint32 data) + { + switch(data) + { + case TYPE_BARREL_DIVERSION: + return Encounter[0]; + case TYPE_THRALL_EVENT: + return Encounter[1]; + case TYPE_THRALL_PART1: + return Encounter[2]; + case TYPE_THRALL_PART2: + return Encounter[3]; + case TYPE_THRALL_PART3: + return Encounter[4]; + case TYPE_THRALL_PART4: + return Encounter[5]; + } + return 0; + } + + uint64 GetData64(uint32 data) + { + switch(data) + { + case DATA_THRALL: + return ThrallGUID; + case DATA_TARETHA: + return TarethaGUID; + } + return 0; + } +}; +InstanceData* GetInstanceData_instance_old_hillsbrad(Map* map) +{ + return new instance_old_hillsbrad(map); +} + +void AddSC_instance_old_hillsbrad() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_old_hillsbrad"; + newscript->GetInstanceData = GetInstanceData_instance_old_hillsbrad; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp new file mode 100644 index 00000000000..a02715785fe --- /dev/null +++ b/src/bindings/scripts/scripts/zone/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp @@ -0,0 +1,916 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Old_Hillsbrad +SD%Complete: 40 +SDComment: All friendly NPC's. Thrall waypoints fairly complete, missing many details, but possible to complete escort. +SDCategory: Caverns of Time, Old Hillsbrad Foothills +EndScriptData */ + +/* ContentData +npc_brazen +npc_erozion +npc_thrall_old_hillsbrad +npc_taretha +EndContentData */ + +#include "precompiled.h" +#include "../../../npc/npc_escortAI.h" +#include "def_old_hillsbrad.h" + +#define QUEST_ENTRY_HILLSBRAD 10282 +#define QUEST_ENTRY_DIVERSION 10283 +#define QUEST_ENTRY_ESCAPE 10284 +#define QUEST_ENTRY_RETURN 10285 +#define ITEM_ENTRY_BOMBS 25853 + +/*###### +## npc_brazen +######*/ + +bool GossipHello_npc_brazen(Player *player, Creature *_Creature) +{ + player->ADD_GOSSIP_ITEM(0, "I am ready to go to Durnholde Keep.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_brazen(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + if( !player->HasItemCount(ITEM_ENTRY_BOMBS,1) ) + player->SEND_GOSSIP_MENU(9780, _Creature->GetGUID()); + else + { + player->CLOSE_GOSSIP_MENU(); + + std::vector nodes; + + nodes.resize(2); + nodes[0] = 115; //from brazen + nodes[1] = 116; //end outside durnholde + player->ActivateTaxiPathTo(nodes); //TaxiPath 534 + } + } + return true; +} + +/*###### +## npc_erozion +######*/ + +bool GossipHello_npc_erozion(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + ScriptedInstance* pInstance = ((ScriptedInstance*)_Creature->GetInstanceData()); + if( pInstance && pInstance->GetData(TYPE_BARREL_DIVERSION) != DONE && !player->HasItemCount(ITEM_ENTRY_BOMBS,1) ) + player->ADD_GOSSIP_ITEM( 0, "I need a pack of Incendiary Bombs.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + if( !player->GetQuestRewardStatus(QUEST_ENTRY_RETURN) && player->GetQuestStatus(QUEST_ENTRY_RETURN) == QUEST_STATUS_COMPLETE ) + player->ADD_GOSSIP_ITEM( 0, "[PH] Teleport please, i'm tired.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + + player->SEND_GOSSIP_MENU(9778, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_erozion(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if( action == GOSSIP_ACTION_INFO_DEF+1 ) + { + ItemPosCountVec dest; + uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, ITEM_ENTRY_BOMBS, 1, false); + if( msg == EQUIP_ERR_OK ) + { + player->StoreNewItem( dest, ITEM_ENTRY_BOMBS, 1, true); + } + player->SEND_GOSSIP_MENU(9515, _Creature->GetGUID()); + } + if( action == GOSSIP_ACTION_INFO_DEF+2 ) + { + player->CLOSE_GOSSIP_MENU(); + } + return true; +} + +/*###### +## npc_thrall_old_hillsbrad +######*/ + +#define SPEED_WALK (0.5f) +#define SPEED_RUN (1.0f) +#define SPEED_MOUNT (1.6f) + +#define THRALL_WEAPON_MODEL 22106 +#define THRALL_WEAPON_INFO 218169346 +#define THRALL_SHIELD_MODEL 18662 +#define THRALL_SHIELD_INFO 234948100 +#define THRALL_MODEL_UNEQUIPPED 17292 +#define THRALL_MODEL_EQUIPPED 18165 + +#define MOB_ENTRY_RIFLE 17820 +#define MOB_ENTRY_WARDEN 17833 +#define MOB_ENTRY_VETERAN 17860 +#define MOB_ENTRY_WATCHMAN 17814 +#define MOB_ENTRY_SENTRY 17815 + +#define MOB_ENTRY_BARN_GUARDSMAN 18092 +#define MOB_ENTRY_BARN_PROTECTOR 18093 +#define MOB_ENTRY_BARN_LOOKOUT 18094 + +#define MOB_ENTRY_CHURCH_GUARDSMAN 23175 +#define MOB_ENTRY_CHURCH_PROTECTOR 23179 +#define MOB_ENTRY_CHURCH_LOOKOUT 23177 + +#define MOB_ENTRY_INN_GUARDSMAN 23176 +#define MOB_ENTRY_INN_PROTECTOR 23180 +#define MOB_ENTRY_INN_LOOKOUT 23178 + +#define SKARLOC_MOUNT 18798 +#define SKARLOC_MOUNT_MODEL 18223 +#define EROZION_ENTRY 18723 + +#define GOSSIP_ID_START 9568 +#define GOSSIP_ID_SKARLOC1 9614 //I'm glad Taretha is alive. We now must find a way to free her... +#define GOSSIP_ITEM_SKARLOC1 "Taretha cannot see you, Thrall." +#define GOSSIP_ID_SKARLOC2 9579 //What do you mean by this? Is Taretha in danger? +#define GOSSIP_ITEM_SKARLOC2 "The situation is rather complicated, Thrall. It would be best for you to head into the mountains now, before more of Blackmoore's men show up. We'll make sure Taretha is safe." +#define GOSSIP_ID_SKARLOC3 9580 + +#define GOSSIP_ID_TARREN 9597 //tarren mill is beyond these trees +#define GOSSIP_ITEM_TARREN "We're ready, Thrall." + +#define GOSSIP_ID_COMPLETE 9578 //Thank you friends, I owe my freedom to you. Where is Taretha? I hoped to see her + +#define THRALL_START_EVENT_PART1 "Very well then. Let's go!" +#define SOUND_START_EVENT 10465 + +#define THRALL_SAY_ARMOR "As long as we're going with a new plan, I may aswell pick up a weapon and some armor." + +#define THRALL_SKARLOC_MEET "A rider approaches!" +#define SOUND_SKARLOC_MEET 10466 +#define THRALL_SKARLOC_TAUNT "I'll never be chained again!" +#define SOUND_SKARLOC_TAUNT 10467 + +#define THRALL_START_EVENT_PART2 "Very well. Tarren Mill lies just west of here. Since time is of the essence..." +#define SOUND_START_EVENT_PART2 10468 +#define THRALL_MOUNTS_UP "Let's ride!" +#define SOUND_MOUNTS_UP 10469 + +#define THRALL_CHURCH_END "Taretha must be in the inn. Let's go." +#define THRALL_MEET_TARETHA "Taretha! What foul magic is this?" + +#define THRALL_EPOCH_WONDER "Who or what was that?" +#define SOUND_EPOCH_WONDER 10470 +#define THRALL_EPOCH_KILL_TARETHA "No!" +#define SOUND_EPOCH_KILL_TARETHA 10471 + +#define THRALL_EVENT_COMPLETE "Goodbye, Taretha. I will never forget your kindness." +#define SOUND_EVENT_COMPLETE 10472 + +#define THRALL_RANDOM_LOW_HP1 "Things are looking grim..." +#define SOUND_RANDOM_LOW_HP1 10458 +#define THRALL_RANDOM_LOW_HP2 "I will fight to the last!" +#define SOUND_RANDOM_LOW_HP2 10459 + +#define THRALL_RANDOM_DIE1 "Taretha..." +#define SOUND_RANDOM_DIE1 10460 +#define THRALL_RANDOM_DIE2 "A good day...to die..." +#define SOUND_RANDOM_DIE2 10461 + +#define THRALL_RANDOM_AGGRO1 "I have earned my freedom!" +#define SOUND_RANDOM_AGGRO1 10448 +#define THRALL_RANDOM_AGGRO2 "This day is long overdue. Out of my way!" +#define SOUND_RANDOM_AGGRO2 10449 +#define THRALL_RANDOM_AGGRO3 "I am a slave no longer!" +#define SOUND_RANDOM_AGGRO3 10450 +#define THRALL_RANDOM_AGGRO4 "Blackmoore has much to answer for!" +#define SOUND_RANDOM_AGGRO4 10451 + +#define THRALL_RANDOM_KILL1 "You have forced my hand!" +#define SOUND_RANDOM_KILL1 10452 +#define THRALL_RANDOM_KILL2 "It should not have come to this!" +#define SOUND_RANDOM_KILL2 10453 +#define THRALL_RANDOM_KILL3 "I did not ask for this!" +#define SOUND_RANDOM_KILL3 10454 + +#define THRALL_LEAVE_COMBAT1 "I am truly in your debt, strangers." +#define SOUND_LEAVE_COMBAT1 10455 +#define THRALL_LEAVE_COMBAT2 "Thank you, strangers. You have given me hope." +#define SOUND_LEAVE_COMBAT2 10456 +#define THRALL_LEAVE_COMBAT3 "I will not waste this chance. I will seek out my destiny." +#define SOUND_LEAVE_COMBAT3 10457 + +struct MANGOS_DLL_DECL npc_thrall_old_hillsbradAI : public npc_escortAI +{ + npc_thrall_old_hillsbradAI(Creature *c) : npc_escortAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + Creature* sum; + uint64 TarethaGUID; + bool LowHp; + bool HadMount; + + void WaypointReached(uint32 i) + { + switch( i ) + { + case 8: + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + sum = m_creature->SummonCreature(18764,2181.87,112.46,89.45,0.26,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + break; + case 9: + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + DoSay(THRALL_SAY_ARMOR, LANG_UNIVERSAL, NULL); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, THRALL_WEAPON_MODEL); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO, THRALL_WEAPON_INFO); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO+1, 781); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, THRALL_SHIELD_MODEL); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO+2, THRALL_SHIELD_INFO); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO+3, 1038); + break; + case 10: + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, THRALL_MODEL_EQUIPPED); + break; + case 11: + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + break; + case 15: + sum = m_creature->SummonCreature(MOB_ENTRY_RIFLE,2200.28,137.37,87.93,5.07,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_WARDEN,2197.44,131.83,87.93,0.78,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_VETERAN,2203.62,135.40,87.93,3.70,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_VETERAN,2200.75,130.13,87.93,1.48,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + break; + case 21: + sum = m_creature->SummonCreature(MOB_ENTRY_RIFLE,2135.80,154.01,67.45,4.98,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_WARDEN,2144.36,151.87,67.74,4.46,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_VETERAN,2142.12,154.41,67.12,4.56,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_VETERAN,2138.08,155.38,67.24,4.60,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + break; + case 25: + sum = m_creature->SummonCreature(MOB_ENTRY_RIFLE,2102.98,192.17,65.24,6.02,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_WARDEN,2108.48,198.75,65.18,5.15,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_VETERAN,2106.11,197.29,65.18,5.63,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_VETERAN,2104.18,194.82,65.18,5.75,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + break; + case 29: + DoSay(THRALL_SKARLOC_MEET, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SKARLOC_MEET); + sum = m_creature->SummonCreature(17862,2036.48,271.22,63.43,5.27,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,30000); + //temporary,skarloc should rather be triggered to walk up to thrall + if( sum ) sum->AI()->AttackStart(m_creature); + break; + case 30: + IsOnHold = true; + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + break; + case 31: + DoSay(THRALL_MOUNTS_UP, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_MOUNTS_UP); + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + DoMount(); + break; + case 37: + //possibly regular patrollers? If so, remove this and let database handle them + sum = m_creature->SummonCreature(MOB_ENTRY_WATCHMAN,2124.26,522.16,56.87,3.99,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_WATCHMAN,2121.69,525.37,57.11,4.01,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_SENTRY,2124.65,524.55,56.63,3.98,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + break; + case 59: + m_creature->SummonCreature(SKARLOC_MOUNT,2488.64,625.77,58.26,4.71,TEMPSUMMON_TIMED_DESPAWN,10000); + DoUnmount(); + HadMount = false; + break; + case 60: + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + //make horsie run off + IsOnHold = true; + m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + if( pInstance ) + pInstance->SetData(TYPE_THRALL_PART2, DONE); + break; + case 64: + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + break; + case 68: + m_creature->SummonCreature(MOB_ENTRY_BARN_PROTECTOR,2500.22,692.60,55.50,2.84,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + m_creature->SummonCreature(MOB_ENTRY_BARN_LOOKOUT,2500.13,696.55,55.51,3.38,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + m_creature->SummonCreature(MOB_ENTRY_BARN_GUARDSMAN,2500.55,693.64,55.50,3.14,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + m_creature->SummonCreature(MOB_ENTRY_BARN_GUARDSMAN,2500.94,695.81,55.50,3.14,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + break; + case 71: + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + break; + case 81: + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + break; + case 83: + sum = m_creature->SummonCreature(MOB_ENTRY_CHURCH_PROTECTOR,2627.33,646.82,56.03,4.28,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_CHURCH_LOOKOUT,2624.14,648.03,56.03,4.50,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_CHURCH_GUARDSMAN,2625.32,649.60,56.03,4.38,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_CHURCH_GUARDSMAN,2627.22,649.00,56.03,4.34,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + break; + case 84: + DoSay(THRALL_CHURCH_END, LANG_UNIVERSAL, NULL); + break; + case 91: + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + case 93: + sum = m_creature->SummonCreature(MOB_ENTRY_INN_PROTECTOR,2652.71,660.31,61.93,1.67,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_INN_LOOKOUT,2648.96,662.59,61.93,0.79,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_INN_GUARDSMAN,2657.36,662.34,61.93,2.68,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + sum = m_creature->SummonCreature(MOB_ENTRY_INN_GUARDSMAN,2656.39,659.77,61.93,2.61,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,5000); + if( sum ) sum->AI()->AttackStart(m_creature); + break; + case 94: + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + //trigger taretha Say("Thrall, you escaped!") + break; + case 95: + DoSay(THRALL_MEET_TARETHA, LANG_UNIVERSAL, NULL); + if( pInstance ) + pInstance->SetData(TYPE_THRALL_PART3,DONE); + IsOnHold = true; + break; + case 96: + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + DoYell(THRALL_EPOCH_WONDER, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_EPOCH_WONDER); + break; + case 97: + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); + DoYell(THRALL_EPOCH_KILL_TARETHA, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_EPOCH_KILL_TARETHA); + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + break; + case 98: + //trigger epoch Yell("Thrall! Come outside and face your fate! ....") + break; + case 106: + { + //trigger taretha to run down outside + /*if( pInstance ) + uint64 TarethaGUID = pInstance->GetData64(DATA_TARETHA); + if( TarethaGUID ) + { + Creature* Taretha = ((Creature*)Unit::GetUnit((*m_creature), TarethaGUID)); + if( Taretha ) + ((npc_escortAI*)(Taretha->AI()))->Start(false, false, true, 0); + }*/ + + if( PlayerGUID ) + { + Unit* player = ((Creature*)Unit::GetUnit((*m_creature), PlayerGUID)); + if( player && player->GetTypeId() == TYPEID_PLAYER ) + ((Player*)player)->KilledMonster(20156,m_creature->GetGUID()); + } + + //alot will happen here, thrall and taretha talk, erozion appear at spot to explain + m_creature->SummonCreature(EROZION_ENTRY,2646.47,680.416,55.38,4.16,TEMPSUMMON_TIMED_DESPAWN,120000); + } + break; + } + } + + void Reset() + { + sum = NULL; + LowHp = false; + + if( HadMount ) + DoMount(); + + if( !IsBeingEscorted ) + { + DoUnmount(); + HadMount = false; + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 0); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO, 0); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO+1, 0); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 0); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO+2, 0); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO+3, 0); + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, THRALL_MODEL_UNEQUIPPED); + } + if( IsBeingEscorted ) + { + switch(rand()%3) + { + case 0: + DoYell(THRALL_LEAVE_COMBAT1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_LEAVE_COMBAT1); + break; + case 1: + DoYell(THRALL_LEAVE_COMBAT2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_LEAVE_COMBAT2); + break; + case 2: + DoYell(THRALL_LEAVE_COMBAT3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_LEAVE_COMBAT3); + break; + } + } + } + void StartWP() + { + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + IsOnHold = false; + } + void DoMount() + { + m_creature->Mount(SKARLOC_MOUNT_MODEL); + m_creature->SetSpeed(MOVE_RUN,SPEED_MOUNT); + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + } + void DoUnmount() + { + m_creature->Unmount(); + m_creature->SetSpeed(MOVE_RUN,SPEED_RUN); + } + void Aggro(Unit* who) + { + switch(rand()%4) + { + case 0: + DoYell(THRALL_RANDOM_AGGRO1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_AGGRO1); + break; + case 1: + DoYell(THRALL_RANDOM_AGGRO2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_AGGRO2); + break; + case 2: + DoYell(THRALL_RANDOM_AGGRO3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_AGGRO3); + break; + case 3: + DoYell(THRALL_RANDOM_AGGRO4,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_AGGRO4); + break; + } + if( m_creature->IsMounted() ) + { + DoUnmount(); + HadMount = true; + } + } + void KilledUnit(Unit *victim) + { + switch(rand()%3) + { + case 0: + DoYell(THRALL_RANDOM_KILL1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_KILL1); + break; + case 1: + DoYell(THRALL_RANDOM_KILL2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_KILL2); + break; + case 2: + DoYell(THRALL_RANDOM_KILL3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_KILL3); + break; + } + } + void JustDied(Unit *slayer) + { + if(slayer == m_creature) // Don't do a yell if he kills self (if player goes too far or at the end). + return; + + switch(rand()%2) + { + case 0: + DoYell(THRALL_RANDOM_DIE1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_DIE1); + break; + case 1: + DoYell(THRALL_RANDOM_DIE2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_DIE2); + break; + } + if( pInstance ) + pInstance->SetData(TYPE_THRALL_EVENT,FAIL); + } + void UpdateAI(const uint32 diff) + { + npc_escortAI::UpdateAI(diff); + + if( InCombat && m_creature->getVictim() ) + { + //add his abilities'n-crap here + + if( !LowHp && ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20) ) + { + switch(rand()%2) + { + case 0: + DoYell(THRALL_RANDOM_LOW_HP1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_LOW_HP1); + break; + case 1: + DoYell(THRALL_RANDOM_LOW_HP2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RANDOM_LOW_HP2); + break; + } + LowHp = true; + } + } + } +}; + +CreatureAI* GetAI_npc_thrall_old_hillsbrad(Creature *_Creature) +{ + npc_thrall_old_hillsbradAI* thrall_walkAI = new npc_thrall_old_hillsbradAI(_Creature); + + thrall_walkAI->AddWaypoint(0, 2230.91, 118.765, 82.2947,5000); + thrall_walkAI->AddWaypoint(1, 2230.33, 114.980, 82.2946); + thrall_walkAI->AddWaypoint(2, 2233.36, 111.057, 82.2996); + thrall_walkAI->AddWaypoint(3, 2231.17, 108.486, 82.6624); + thrall_walkAI->AddWaypoint(4, 2220.22, 114.605, 89.4264); + thrall_walkAI->AddWaypoint(5, 2215.23, 115.990, 89.4549); + thrall_walkAI->AddWaypoint(6, 2210.00, 106.849, 89.4549); + thrall_walkAI->AddWaypoint(7, 2205.66, 105.234, 89.4549); + //spawn armorer + thrall_walkAI->AddWaypoint(8, 2192.26, 112.618, 89.4549); + + //get weapon + thrall_walkAI->AddWaypoint(9, 2181.28, 118.612, 89.4549,8000); + //get armor + thrall_walkAI->AddWaypoint(10, 2181.62, 120.385, 89.4549,5000); + + thrall_walkAI->AddWaypoint(11, 2189.44, 113.922, 89.4549); + thrall_walkAI->AddWaypoint(12, 2195.63, 110.584, 89.4549); + thrall_walkAI->AddWaypoint(13, 2201.09, 115.115, 89.4549); + thrall_walkAI->AddWaypoint(14, 2204.34, 121.036, 89.4355); + //first ambush + thrall_walkAI->AddWaypoint(15, 2208.66, 129.127, 87.9560); + thrall_walkAI->AddWaypoint(16, 2193.09, 137.940, 88.2164); + thrall_walkAI->AddWaypoint(17, 2173.39, 149.064, 87.9227); + thrall_walkAI->AddWaypoint(18, 2164.25, 137.965, 85.0595); + thrall_walkAI->AddWaypoint(19, 2149.31, 125.645, 77.0858); + thrall_walkAI->AddWaypoint(20, 2142.78, 127.173, 75.5954); + //second ambush + thrall_walkAI->AddWaypoint(21, 2139.28, 133.952, 73.6386); + thrall_walkAI->AddWaypoint(22, 2139.54, 155.235, 67.1269); + thrall_walkAI->AddWaypoint(23, 2145.38, 167.551, 64.8974); + thrall_walkAI->AddWaypoint(24, 2134.28, 175.304, 67.9446); + thrall_walkAI->AddWaypoint(25, 2118.08, 187.387, 68.8141); + //third ambush + thrall_walkAI->AddWaypoint(26, 2105.88, 195.461, 65.1854); + thrall_walkAI->AddWaypoint(27, 2096.77, 196.939, 65.2117); + thrall_walkAI->AddWaypoint(28, 2083.90, 209.395, 64.8736); + //in front of keeps gate, meeting scarloc + thrall_walkAI->AddWaypoint(29, 2067.84, 224.376, 64.8022,30000); + + //ref point after skarloc fight + thrall_walkAI->AddWaypoint(30, 2055.40, 242.90, 63.3418); + + //mount up! + thrall_walkAI->AddWaypoint(31, 2039.20, 266.460, 63.0182,10000); + thrall_walkAI->AddWaypoint(32, 2011.77, 278.478, 65.3388); + thrall_walkAI->AddWaypoint(33, 2005.08, 289.676, 66.1179); + thrall_walkAI->AddWaypoint(34, 2033.11, 337.450, 66.0948); + thrall_walkAI->AddWaypoint(35, 2070.30, 416.208, 66.0893); + thrall_walkAI->AddWaypoint(36, 2086.76, 469.768, 65.9182); + //possible road ambush + thrall_walkAI->AddWaypoint(37, 2101.70, 497.955, 61.7881); + + thrall_walkAI->AddWaypoint(38, 2133.39, 530.933, 55.3700,5000); + thrall_walkAI->AddWaypoint(39, 2157.91, 559.635, 48.5157); + thrall_walkAI->AddWaypoint(40, 2167.34, 586.191, 42.4394); + thrall_walkAI->AddWaypoint(41, 2174.17, 637.643, 33.9002); + thrall_walkAI->AddWaypoint(42, 2179.31, 656.053, 34.723); + thrall_walkAI->AddWaypoint(43, 2183.65, 670.941, 34.0318); + thrall_walkAI->AddWaypoint(44, 2201.50, 668.616, 36.1236); + thrall_walkAI->AddWaypoint(45, 2221.56, 652.747, 36.6153); + thrall_walkAI->AddWaypoint(46, 2238.97, 640.125, 37.2214); + thrall_walkAI->AddWaypoint(47, 2251.17, 620.574, 40.1473); + thrall_walkAI->AddWaypoint(48, 2261.98, 595.303, 41.4117); + thrall_walkAI->AddWaypoint(49, 2278.67, 560.172, 38.9090); + thrall_walkAI->AddWaypoint(50, 2336.72, 528.327, 40.9369); + thrall_walkAI->AddWaypoint(51, 2381.04, 519.612, 37.7312); + thrall_walkAI->AddWaypoint(52, 2412.20, 515.425, 39.2068); + thrall_walkAI->AddWaypoint(53, 2452.39, 516.174, 42.9387); + thrall_walkAI->AddWaypoint(54, 2467.38, 539.389, 47.4992); + thrall_walkAI->AddWaypoint(55, 2470.70, 554.333, 46.6668); + thrall_walkAI->AddWaypoint(56, 2478.07, 575.321, 55.4549); + thrall_walkAI->AddWaypoint(57, 2480.00, 585.408, 56.6921); + thrall_walkAI->AddWaypoint(58, 2482.67, 608.817, 55.6643); + //demount + thrall_walkAI->AddWaypoint(59, 2485.62, 626.061, 58.0132,2000); + //scare the shit out of horse, so it'll run off + thrall_walkAI->AddWaypoint(60, 2486.91, 626.356, 58.0761); + + thrall_walkAI->AddWaypoint(61, 2488.58, 660.940, 57.3913); + thrall_walkAI->AddWaypoint(62, 2502.56, 686.059, 55.6252); + thrall_walkAI->AddWaypoint(63, 2502.08, 694.360, 55.5083); + thrall_walkAI->AddWaypoint(64, 2491.46, 694.321, 55.7163); + thrall_walkAI->AddWaypoint(65, 2491.10, 703.300, 55.7630); + thrall_walkAI->AddWaypoint(66, 2485.64, 702.992, 55.7917); + + thrall_walkAI->AddWaypoint(67, 2479.10, 695.291, 55.7901,10000); + //spawn mobs + thrall_walkAI->AddWaypoint(68, 2476.75, 693.689, 55.7960); + thrall_walkAI->AddWaypoint(69, 2475.39, 695.983, 55.8146); + thrall_walkAI->AddWaypoint(70, 2477.75, 694.473, 55.7945); + //meet mobs in doorway + thrall_walkAI->AddWaypoint(71, 2481.27, 697.747, 55.7910); + + thrall_walkAI->AddWaypoint(72, 2486.31, 703.131, 55.7861,5000); + thrall_walkAI->AddWaypoint(73, 2490.76, 703.511, 55.7662); + thrall_walkAI->AddWaypoint(74, 2491.30, 694.792, 55.7195); + thrall_walkAI->AddWaypoint(75, 2518.69, 693.876, 55.1383); + thrall_walkAI->AddWaypoint(76, 2531.33, 681.914, 55.1383); + thrall_walkAI->AddWaypoint(77, 2568.25, 682.654, 55.1778); + thrall_walkAI->AddWaypoint(78, 2589.61, 689.981, 55.1421); + thrall_walkAI->AddWaypoint(79, 2634.74, 679.833, 54.6613); + thrall_walkAI->AddWaypoint(80, 2630.41, 661.464, 54.2761); + thrall_walkAI->AddWaypoint(81, 2629.00, 656.982, 56.0651); + //stop in church + thrall_walkAI->AddWaypoint(82, 2620.84, 633.007, 56.0300,3000); + //summon + thrall_walkAI->AddWaypoint(83, 2622.99, 639.178, 56.0300); + + thrall_walkAI->AddWaypoint(84, 2628.73, 656.693, 56.0610,5000); + thrall_walkAI->AddWaypoint(85, 2630.34, 661.135, 54.2738); + thrall_walkAI->AddWaypoint(86, 2635.38, 672.243, 54.4508); + thrall_walkAI->AddWaypoint(87, 2644.13, 668.158, 55.3797); + thrall_walkAI->AddWaypoint(88, 2646.82, 666.740, 56.9898); + thrall_walkAI->AddWaypoint(89, 2658.22, 665.432, 57.1725); + thrall_walkAI->AddWaypoint(90, 2661.88, 674.849, 57.1725); + thrall_walkAI->AddWaypoint(91, 2656.23, 677.208, 57.1725); + + thrall_walkAI->AddWaypoint(92, 2652.28, 670.270, 61.9353); + //summon inn + thrall_walkAI->AddWaypoint(93, 2650.79, 664.290, 61.9302); + thrall_walkAI->AddWaypoint(94, 2658.19, 660.454, 61.9320,5000); + //speak with Taretha + thrall_walkAI->AddWaypoint(95, 2660.57, 659.173, 61.9370); + + //epoch calls + thrall_walkAI->AddWaypoint(96, 2658.19, 660.454, 61.9320,5000); + //taretha "dies" + thrall_walkAI->AddWaypoint(97, 2659.84, 659.482, 61.9361,5000); + + thrall_walkAI->AddWaypoint(98, 2654.28, 662.722, 61.9313); + thrall_walkAI->AddWaypoint(99, 2652.37, 670.561, 61.9368); + thrall_walkAI->AddWaypoint(100, 2656.05, 676.761, 57.1727); + thrall_walkAI->AddWaypoint(101, 2658.49, 677.166, 57.1727); + thrall_walkAI->AddWaypoint(102, 2659.28, 667.117, 57.1727); + thrall_walkAI->AddWaypoint(103, 2649.71, 665.387, 57.1727); + //he's outside inn here + thrall_walkAI->AddWaypoint(104, 2634.79, 672.964, 54.4577); + + //getting ready here, must start attack before 30secs up + thrall_walkAI->AddWaypoint(105, 2635.06, 673.892, 54.4713,30000); + + //ref point, will move here when all dead and meet Taretha + thrall_walkAI->AddWaypoint(106, 2634.79, 672.964, 54.4577,60000); + + //run off + thrall_walkAI->AddWaypoint(107, 2631.72, 665.629, 54.2923); + thrall_walkAI->AddWaypoint(108, 2647.40, 640.530, 55.7634); + + return (CreatureAI*)thrall_walkAI; +} + +bool GossipHello_npc_thrall_old_hillsbrad(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + ScriptedInstance* pInstance = ((ScriptedInstance*)_Creature->GetInstanceData()); + if( pInstance ) + { + //if( pInstance->GetData(TYPE_BARREL_DIVERSION) == DONE && pInstance->GetData(TYPE_THRALL_EVENT) == NOT_STARTED ) + if( pInstance->GetData(TYPE_THRALL_EVENT) == NOT_STARTED ) + { + player->ADD_GOSSIP_ITEM( 0, "[PH] Start walking.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(GOSSIP_ID_START, _Creature->GetGUID()); + } + if( pInstance->GetData(TYPE_THRALL_PART1) == DONE && !pInstance->GetData(TYPE_THRALL_PART2) ) + { + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_SKARLOC1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(GOSSIP_ID_SKARLOC1, _Creature->GetGUID()); + } + if( pInstance->GetData(TYPE_THRALL_PART2) == DONE && !pInstance->GetData(TYPE_THRALL_PART3) ) + { + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_TARREN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->SEND_GOSSIP_MENU(GOSSIP_ID_TARREN, _Creature->GetGUID()); + } + } + return true; +} + +bool GossipSelect_npc_thrall_old_hillsbrad(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + ScriptedInstance* pInstance = ((ScriptedInstance*)_Creature->GetInstanceData()); + switch( action ) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->CLOSE_GOSSIP_MENU(); + pInstance->SetData(TYPE_THRALL_EVENT,IN_PROGRESS); + pInstance->SetData(TYPE_THRALL_PART1,IN_PROGRESS); + + _Creature->Say(THRALL_START_EVENT_PART1, LANG_UNIVERSAL, 0); + ((npc_thrall_old_hillsbradAI*)_Creature->AI())->DoPlaySoundToSet(_Creature,SOUND_START_EVENT); + + ((npc_escortAI*)(_Creature->AI()))->Start(true, true, true, player->GetGUID()); + break; + + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_SKARLOC2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+20); + player->SEND_GOSSIP_MENU(GOSSIP_ID_SKARLOC2, _Creature->GetGUID()); + break; + + case GOSSIP_ACTION_INFO_DEF+20: + player->SEND_GOSSIP_MENU(GOSSIP_ID_SKARLOC3, _Creature->GetGUID()); + _Creature->SummonCreature(SKARLOC_MOUNT,2038.81,270.26,63.20,5.41,TEMPSUMMON_TIMED_DESPAWN,12000); + pInstance->SetData(TYPE_THRALL_PART2,IN_PROGRESS); + + _Creature->Say(THRALL_START_EVENT_PART2, LANG_UNIVERSAL, 0); + ((npc_thrall_old_hillsbradAI*)_Creature->AI())->DoPlaySoundToSet(_Creature,SOUND_START_EVENT_PART2); + + ((npc_thrall_old_hillsbradAI*)_Creature->AI())->StartWP(); + break; + + case GOSSIP_ACTION_INFO_DEF+3: + player->CLOSE_GOSSIP_MENU(); + pInstance->SetData(TYPE_THRALL_PART3,IN_PROGRESS); + ((npc_thrall_old_hillsbradAI*)_Creature->AI())->StartWP(); + break; + } + return true; +} + +/*###### +## npc_taretha +######*/ + +#define GOSSIP_ID_EPOCH1 9610 //Thank you for helping Thrall escape, friends. Now I only hope +#define GOSSIP_ITEM_EPOCH1 "Strange wizard?" +#define GOSSIP_ID_EPOCH2 9613 //Yes, friends. This man was no wizard of +#define GOSSIP_ITEM_EPOCH2 "We'll get you out. Taretha. Don't worry. I doubt the wizard would wander too far away." + +#define TARETHA_FREE "I'm free! Thank you all!" + +struct MANGOS_DLL_DECL npc_tarethaAI : public npc_escortAI +{ + npc_tarethaAI(Creature *c) : npc_escortAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + void WaypointReached(uint32 i) + { + switch( i ) + { + case 6: + m_creature->Say(TARETHA_FREE, LANG_UNIVERSAL, 0); + break; + case 7: + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_CHEER); + break; + } + } + void Reset() {} + void Aggro(Unit* who) {} + +}; +CreatureAI* GetAI_npc_taretha(Creature *_Creature) +{ + npc_tarethaAI* taretha_walkAI = new npc_tarethaAI(_Creature); + + taretha_walkAI->AddWaypoint(0, 2650.06, 665.473, 61.9305); + taretha_walkAI->AddWaypoint(1, 2652.44, 670.761, 61.9370); + taretha_walkAI->AddWaypoint(2, 2655.96, 676.913, 57.1725); + taretha_walkAI->AddWaypoint(3, 2659.40, 677.317, 57.1725); + taretha_walkAI->AddWaypoint(4, 2651.75, 664.482, 57.1725); + taretha_walkAI->AddWaypoint(5, 2647.49, 666.595, 57.0824); + taretha_walkAI->AddWaypoint(6, 2644.37, 668.167, 55.4182); + taretha_walkAI->AddWaypoint(7, 2640.96, 669.890, 54.7567,60000); + + return (CreatureAI*)taretha_walkAI; +} + +bool GossipHello_npc_taretha(Player *player, Creature *_Creature) +{ + ScriptedInstance* pInstance = ((ScriptedInstance*)_Creature->GetInstanceData()); + if( pInstance && pInstance->GetData(TYPE_THRALL_PART3) == DONE && pInstance->GetData(TYPE_THRALL_PART4) == NOT_STARTED) + { + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_EPOCH1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(GOSSIP_ID_EPOCH1, _Creature->GetGUID()); + } + return true; +} + +bool GossipSelect_npc_taretha(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + ScriptedInstance* pInstance = ((ScriptedInstance*)_Creature->GetInstanceData()); + if( action == GOSSIP_ACTION_INFO_DEF+1 ) + { + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_EPOCH2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(GOSSIP_ID_EPOCH2, _Creature->GetGUID()); + } + if( action == GOSSIP_ACTION_INFO_DEF+2 ) + { + player->CLOSE_GOSSIP_MENU(); + + if( pInstance->GetData(TYPE_THRALL_EVENT) == IN_PROGRESS ) + { + pInstance->SetData(TYPE_THRALL_PART4,IN_PROGRESS); + _Creature->SummonCreature(18096,2639.13,698.55,65.43,4.59,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,120000); + + uint64 ThrallGUID = pInstance->GetData64(DATA_THRALL); + if(ThrallGUID) + { + Creature* Thrall = ((Creature*)Unit::GetUnit((*_Creature), ThrallGUID)); + if(Thrall) + ((npc_thrall_old_hillsbradAI*)Thrall->AI())->StartWP(); + } + } + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_old_hillsbrad() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_brazen"; + newscript->pGossipHello = &GossipHello_npc_brazen; + newscript->pGossipSelect = &GossipSelect_npc_brazen; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_erozion"; + newscript->pGossipHello = &GossipHello_npc_erozion; + newscript->pGossipSelect = &GossipSelect_npc_erozion; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_thrall_old_hillsbrad"; + newscript->pGossipHello = &GossipHello_npc_thrall_old_hillsbrad; + newscript->pGossipSelect = &GossipSelect_npc_thrall_old_hillsbrad; + newscript->GetAI = GetAI_npc_thrall_old_hillsbrad; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_taretha"; + newscript->pGossipHello = &GossipHello_npc_taretha; + newscript->pGossipSelect = &GossipSelect_npc_taretha; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp new file mode 100644 index 00000000000..d21c3836d00 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp @@ -0,0 +1,559 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Fathomlord_Karathress +SD%Complete: 50 +SDComment: Missing Multishot, pet, Totems, Windfury, Whirlwind +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "def_serpent_shrine.h" + +//Karathress spells +#define SPELL_CATACLYSMIC_BOLT 38441 +#define SPELL_POWER_OF_SHARKKIS 38455 +#define SPELL_POWER_OF_TIDALVESS 38452 +#define SPELL_POWER_OF_CARIBDIS 38451 +#define SPELL_ENRAGE 24318 +//Sharkkis spells +#define SPELL_LEECHING_THROW 29436 +#define SPELL_THE_BEAST_WITHIN 38373 +//Tidalvess spells +#define SPELL_FROST_SHOCK 38234 +//Caribdis Spells +#define SPELL_WATER_BOLT_VOLLEY 38335 +#define SPELL_TIDAL_SURGE 38353 +#define SPELL_HEAL 41386 + +#define SAY_AGGRO "Guards, attention! We have visitors..." +#define SAY_SLAY1 "I am rid of you." +#define SAY_GAIN_ABILITY1 "I am more powerful than ever!" +#define SAY_GAIN_ABILITY2 "Go on, kill them! I'll be the better for it!" +#define SAY_GAIN_ABILITY3 "More knowledge, more power!" +#define SAY_DEATH "Her ... excellency ... awaits!" + +#define SOUND_AGGRO 11277 +#define SOUND_PLYR_ATTACK 11278 +#define SOUND_GAIN_ABILITY1 11279 +#define SOUND_GAIN_ABILITY2 11280 +#define SOUND_GAIN_ABILITY3 11281 +#define SOUND_SLAY1 11282 +#define SOUND_SLAY2 11283 +#define SOUND_SLAY3 11284 +#define SOUND_DEATH 11285 + +//entry and position for Seer Olum +#define SEER_OLUM 22820 +#define OLUM_X 446.78f +#define OLUM_Y -542.76f +#define OLUM_Z -7.54773f +#define OLUM_O 0.401581f + +//Fathom-Lord Karathress AI +struct MANGOS_DLL_DECL boss_fathomlord_karathressAI : public ScriptedAI +{ + boss_fathomlord_karathressAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Advisors[0] = 0; + Advisors[1] = 0; + Advisors[2] = 0; + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 CataclysmicBolt_Timer; + uint32 Enrage_Timer; + + uint64 Advisors[3]; + + void Reset() + { + CataclysmicBolt_Timer = 10000; + Enrage_Timer = 600000; //10 minutes + + Creature* Advisor; + for(uint8 i = 0; i < 3; ++i) + if(Advisors[i]) + { + Advisor = ((Creature*)Unit::GetUnit(*m_creature, Advisors[i])); + if(Advisor) + { + if(Advisor->isAlive()) + { + Advisor->DealDamage(Advisor, Advisor->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + Advisor->RemoveCorpse(); + Advisor->Respawn(); + } + Advisor->AI()->EnterEvadeMode(); + } + } + + if( pInstance ) + pInstance->SetData(DATA_KARATHRESSEVENT, NOT_STARTED); + } + + void EventSharkkisDeath() + { + DoPlaySoundToSet(m_creature, SOUND_GAIN_ABILITY1); + DoYell(SAY_GAIN_ABILITY1, LANG_UNIVERSAL, NULL); + DoCast(m_creature, SPELL_POWER_OF_SHARKKIS); + } + + void EventTidalvessDeath() + { + DoPlaySoundToSet(m_creature, SOUND_GAIN_ABILITY2); + DoYell(SAY_GAIN_ABILITY2, LANG_UNIVERSAL, NULL); + DoCast(m_creature, SPELL_POWER_OF_TIDALVESS); + } + + void EventCaribdisDeath() + { + DoPlaySoundToSet(m_creature, SOUND_GAIN_ABILITY3); + DoYell(SAY_GAIN_ABILITY3, LANG_UNIVERSAL, NULL); + DoCast(m_creature, SPELL_POWER_OF_CARIBDIS); + } + + void GetAdvisors() + { + if(!pInstance) + return; + + Advisors[0] = pInstance->GetData64(DATA_SHARKKIS); + Advisors[1] = pInstance->GetData64(DATA_TIDALVESS); + Advisors[2] = pInstance->GetData64(DATA_CARIBDIS); + } + + void StartEvent(Unit *who) + { + if(!pInstance) + return; + + GetAdvisors(); + + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + + pInstance->SetData64(DATA_KARATHRESSEVENT_STARTER, who->GetGUID()); + pInstance->SetData(DATA_KARATHRESSEVENT, IN_PROGRESS); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%3) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + DoYell(SAY_SLAY1, LANG_UNIVERSAL, NULL); + break; + case 1: + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + case 2: + DoPlaySoundToSet(m_creature, SOUND_SLAY3); + break; + } + } + + void JustDied(Unit *killer) + { + DoPlaySoundToSet(m_creature, SOUND_DEATH); + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + + if( pInstance ) + pInstance->SetData(DATA_FATHOMLORDKARATHRESSEVENT, NOT_STARTED); + + //support for quest 10944 + m_creature->SummonCreature(SEER_OLUM, OLUM_X, OLUM_Y, OLUM_Z, OLUM_O, TEMPSUMMON_TIMED_DESPAWN, 3600000); + } + + void Aggro(Unit *who) + { + StartEvent(who); + } + + void UpdateAI(const uint32 diff) + { + //Only if not incombat check if the event is started + if(!InCombat && pInstance && pInstance->GetData(DATA_KARATHRESSEVENT)) + { + Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESSEVENT_STARTER)); + + if(target) + { + DoStartAttackAndMovement(target); + + GetAdvisors(); + + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + } + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //someone evaded! + if(pInstance && !pInstance->GetData(DATA_KARATHRESSEVENT)) + EnterEvadeMode(); + + //CataclysmicBolt_Timer + if(CataclysmicBolt_Timer < diff) + { + //select a random unit other than the main tank + Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 1); + + //if there aren't other units, cast on the tank + if(!target) + target = m_creature->getVictim(); + + int32 dmg = target->GetMaxHealth() / 2; + m_creature->CastCustomSpell(target, SPELL_CATACLYSMIC_BOLT, &dmg, NULL, NULL, false, NULL, NULL, m_creature->GetGUID()); + + CataclysmicBolt_Timer = 10000; + }else CataclysmicBolt_Timer -= diff; + + //Enrage_Timer + if(Enrage_Timer < diff) + { + DoCast(m_creature, SPELL_ENRAGE); + Enrage_Timer = 90000; + }else Enrage_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Fathom-Guard Sharkkis AI +struct MANGOS_DLL_DECL boss_fathomguard_sharkkisAI : public ScriptedAI +{ + boss_fathomguard_sharkkisAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 LeechingThrow_Timer; + uint32 TheBeastWithin_Timer; + + void Reset() + { + LeechingThrow_Timer = 20000; + TheBeastWithin_Timer = 30000; + + if( pInstance ) + pInstance->SetData(DATA_KARATHRESSEVENT, NOT_STARTED); + } + + void JustDied(Unit *victim) + { + if(pInstance) + { + Creature *Karathress = NULL; + Karathress = (Creature*)(Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESS))); + + if(Karathress) + ((boss_fathomlord_karathressAI*)Karathress->AI())->EventSharkkisDeath(); + } + } + + void Aggro(Unit *who) + { + if(pInstance) + { + pInstance->SetData64(DATA_KARATHRESSEVENT_STARTER, who->GetGUID()); + pInstance->SetData(DATA_KARATHRESSEVENT, IN_PROGRESS); + } + } + + void UpdateAI(const uint32 diff) + { + //Only if not incombat check if the event is started + if(!InCombat && pInstance && pInstance->GetData(DATA_KARATHRESSEVENT)) + { + Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESSEVENT_STARTER)); + + if(target) + { + DoStartAttackAndMovement(target); + } + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //someone evaded! + if(pInstance && !pInstance->GetData(DATA_KARATHRESSEVENT)) + EnterEvadeMode(); + + //LeechingThrow_Timer + if(LeechingThrow_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_LEECHING_THROW); + LeechingThrow_Timer = 20000; + }else LeechingThrow_Timer -= diff; + + //TheBeastWithin_Timer + if(TheBeastWithin_Timer < diff) + { + DoCast(m_creature, SPELL_THE_BEAST_WITHIN); + TheBeastWithin_Timer = 30000; + }else TheBeastWithin_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Fathom-Guard Tidalvess AI +struct MANGOS_DLL_DECL boss_fathomguard_tidalvessAI : public ScriptedAI +{ + boss_fathomguard_tidalvessAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 FrostShock_Timer; + + void Reset() + { + FrostShock_Timer = 25000; + + if( pInstance ) + pInstance->SetData(DATA_KARATHRESSEVENT, NOT_STARTED); + } + + void JustDied(Unit *victim) + { + if(pInstance) + { + Creature *Karathress = NULL; + Karathress = (Creature*)(Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESS))); + + if(Karathress) + ((boss_fathomlord_karathressAI*)Karathress->AI())->EventTidalvessDeath(); + } + } + + void Aggro(Unit *who) + { + if(pInstance) + { + pInstance->SetData64(DATA_KARATHRESSEVENT_STARTER, who->GetGUID()); + pInstance->SetData(DATA_KARATHRESSEVENT, IN_PROGRESS); + } + } + + void UpdateAI(const uint32 diff) + { + //Only if not incombat check if the event is started + if(!InCombat && pInstance && pInstance->GetData(DATA_KARATHRESSEVENT)) + { + Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESSEVENT_STARTER)); + + if(target) + { + DoStartAttackAndMovement(target); + } + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //someone evaded! + if(pInstance && !pInstance->GetData(DATA_KARATHRESSEVENT)) + EnterEvadeMode(); + + //FrostShock_Timer + if(FrostShock_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FROST_SHOCK); + FrostShock_Timer = 25000+rand()%5000; + }else FrostShock_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Fathom-Guard Caribdis AI +struct MANGOS_DLL_DECL boss_fathomguard_caribdisAI : public ScriptedAI +{ + boss_fathomguard_caribdisAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 WaterBoltVolley_Timer; + uint32 TidalSurge_Timer; + uint32 Heal_Timer; + + void Reset() + { + WaterBoltVolley_Timer = 35000; + TidalSurge_Timer = 15000+rand()%5000; + Heal_Timer = 55000; + + if(pInstance) + pInstance->SetData(DATA_KARATHRESSEVENT, NOT_STARTED); + } + + void JustDied(Unit *victim) + { + if(pInstance) + { + Creature *Karathress = NULL; + Karathress = (Creature*)(Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESS))); + + if(Karathress) + ((boss_fathomlord_karathressAI*)Karathress->AI())->EventCaribdisDeath(); + } + } + + void Aggro(Unit *who) + { + if(pInstance) + { + pInstance->SetData64(DATA_KARATHRESSEVENT_STARTER, who->GetGUID()); + pInstance->SetData(DATA_KARATHRESSEVENT, IN_PROGRESS); + } + } + + void UpdateAI(const uint32 diff) + { + //Only if not incombat check if the event is started + if(!InCombat && pInstance && pInstance->GetData(DATA_KARATHRESSEVENT)) + { + Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESSEVENT_STARTER)); + + if(target) + { + DoStartAttackAndMovement(target); + } + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //someone evaded! + if(pInstance && !pInstance->GetData(DATA_KARATHRESSEVENT)) + EnterEvadeMode(); + + //WaterBoltVolley_Timer + if(WaterBoltVolley_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_WATER_BOLT_VOLLEY); + WaterBoltVolley_Timer = 30000; + }else WaterBoltVolley_Timer -= diff; + + //TidalSurge_Timer + if(TidalSurge_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_TIDAL_SURGE); + TidalSurge_Timer = 15000+rand()%5000; + }else TidalSurge_Timer -= diff; + + //Heal_Timer + if(Heal_Timer < diff) + { + // It can be cast on any of the mobs + Unit *pUnit = NULL; + + if(pInstance) + { + switch(rand()%4) + { + case 0: + pUnit = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESS)); + break; + case 1: + pUnit = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_SHARKKIS)); + break; + case 2: + pUnit = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_TIDALVESS)); + break; + case 3: + pUnit = m_creature; + break; + } + }else pUnit = m_creature; + + if(pUnit && pUnit->isAlive()) + DoCast(pUnit, SPELL_HEAL); + + Heal_Timer = 60000; + }else Heal_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_fathomlord_karathress(Creature *_Creature) +{ + return new boss_fathomlord_karathressAI (_Creature); +} + +CreatureAI* GetAI_boss_fathomguard_sharkkis(Creature *_Creature) +{ + return new boss_fathomguard_sharkkisAI (_Creature); +} + +CreatureAI* GetAI_boss_fathomguard_tidalvess(Creature *_Creature) +{ + return new boss_fathomguard_tidalvessAI (_Creature); +} + +CreatureAI* GetAI_boss_fathomguard_caribdis(Creature *_Creature) +{ + return new boss_fathomguard_caribdisAI (_Creature); +} + +void AddSC_boss_fathomlord_karathress() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_fathomlord_karathress"; + newscript->GetAI = GetAI_boss_fathomlord_karathress; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_fathomguard_sharkkis"; + newscript->GetAI = GetAI_boss_fathomguard_sharkkis; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_fathomguard_tidalvess"; + newscript->GetAI = GetAI_boss_fathomguard_tidalvess; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_fathomguard_caribdis"; + newscript->GetAI = GetAI_boss_fathomguard_caribdis; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp new file mode 100644 index 00000000000..d9bc9429d14 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp @@ -0,0 +1,357 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Hydross_The_Unstable +SD%Complete: 90 +SDComment: Some details and adjustments left to do, probably nothing major. Spawns may be spawned in different way/location. +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "def_serpent_shrine.h" + +#define SWITCH_RADIUS 18 + +#define MODEL_CORRUPT 20609 +#define MODEL_CLEAN 20162 + +#define SPELL_WATER_TOMB 38235 +#define SPELL_MARK_OF_HYDROSS1 38215 +#define SPELL_MARK_OF_HYDROSS2 38216 +#define SPELL_MARK_OF_HYDROSS3 38217 +#define SPELL_MARK_OF_HYDROSS4 38218 +#define SPELL_MARK_OF_HYDROSS5 38231 +#define SPELL_MARK_OF_HYDROSS6 40584 +#define SPELL_MARK_OF_CORRUPTION1 38219 +#define SPELL_MARK_OF_CORRUPTION2 38220 +#define SPELL_MARK_OF_CORRUPTION3 38221 +#define SPELL_MARK_OF_CORRUPTION4 38222 +#define SPELL_MARK_OF_CORRUPTION5 38230 +#define SPELL_MARK_OF_CORRUPTION6 40583 +#define SPELL_VILE_SLUDGE 38246 +#define SPELL_ENRAGE 27680 //this spell need verification +#define SPELL_SUMMON_WATER_ELEMENT 36459 //not in use yet(in use ever?) +#define SPELL_ELEMENTAL_SPAWNIN 25035 +//#define SPELL_BLUE_BEAM 38015 //channeled Hydross Beam Helper (not in use yet) + +#define ENTRY_PURE_SPAWN 22035 +#define ENTRY_TAINTED_SPAWN 22036 + +#define SAY_AGGRO "I cannot allow you to interfere!" +#define SAY_SWITCH_TO_CLEAN "Better, much better." +#define SAY_CLEAN_SLAY1 "They have forced me to this..." +#define SAY_CLEAN_SLAY2 "I have no choice." +#define SAY_CLEAN_DEATH "I am... released..." +#define SAY_SWITCH_TO_CORRUPT "Aaghh, the poison..." +#define SAY_CORRUPT_SLAY1 "I will purge you from this place." +#define SAY_CORRUPT_SLAY2 "You are no better than they!" +#define SAY_CORRUPT_DEATH "You are the disease, not I" + +#define SOUND_AGGRO 11289 +#define SOUND_SWITCH_TO_CLEAN 11290 +#define SOUND_CLEAN_SLAY1 11291 +#define SOUND_CLEAN_SLAY2 11292 +#define SOUND_CLEAN_DEATH 11293 +#define SOUND_SWITCH_TO_CORRUPT 11297 +#define SOUND_CORRUPT_SLAY1 11298 +#define SOUND_CORRUPT_SLAY2 11299 +#define SOUND_CORRUPT_DEATH 11300 + +#define HYDROSS_X -239.439 +#define HYDROSS_Y -363.481 + +#define SPAWN_X_DIFF1 6.934003 +#define SPAWN_Y_DIFF1 -11.255012 +#define SPAWN_X_DIFF2 -6.934003 +#define SPAWN_Y_DIFF2 11.255012 +#define SPAWN_X_DIFF3 -12.577011 +#define SPAWN_Y_DIFF3 -4.72702 +#define SPAWN_X_DIFF4 12.577011 +#define SPAWN_Y_DIFF4 4.72702 + +struct MANGOS_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI +{ + boss_hydross_the_unstableAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 PosCheck_Timer; + uint32 MarkOfHydross_Timer; + uint32 MarkOfCorruption_Timer; + uint32 WaterTomb_Timer; + uint32 VileSludge_Timer; + uint32 MarkOfHydross_Count; + uint32 MarkOfCorruption_Count; + uint32 EnrageTimer; + bool CorruptedForm; + + void Reset() + { + PosCheck_Timer = 2500; + MarkOfHydross_Timer = 15000; + MarkOfCorruption_Timer = 15000; + WaterTomb_Timer = 7000; + VileSludge_Timer = 7000; + MarkOfHydross_Count = 0; + MarkOfCorruption_Count = 0; + EnrageTimer = 600000; + + CorruptedForm = false; + m_creature->SetMeleeDamageSchool(SPELL_SCHOOL_FROST); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, MODEL_CLEAN); + + if( pInstance ) + pInstance->SetData(DATA_HYDROSSTHEUNSTABLEEVENT, NOT_STARTED); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + + if( pInstance ) + pInstance->SetData(DATA_HYDROSSTHEUNSTABLEEVENT, IN_PROGRESS); + } + + void KilledUnit(Unit *victim) + { + if(CorruptedForm) + switch(rand()%2) + { + case 0: + DoYell(SAY_CORRUPT_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CORRUPT_SLAY1); + break; + case 1: + DoYell(SAY_CORRUPT_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CORRUPT_SLAY2); + break; + } + else + { + switch(rand()%2) + { + case 0: + DoYell(SAY_CLEAN_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CLEAN_SLAY1); + break; + case 1: + DoYell(SAY_CLEAN_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CLEAN_SLAY2); + break; + } + } + } + + void JustSummoned(Creature* summoned) + { + if( summoned->GetEntry() == ENTRY_PURE_SPAWN ) + summoned->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true); + if( summoned->GetEntry() == ENTRY_TAINTED_SPAWN ) + summoned->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true); + + summoned->CastSpell(summoned,SPELL_ELEMENTAL_SPAWNIN,true); + } + + void JustDied(Unit *victim) + { + if( CorruptedForm ) + { + DoYell(SAY_CORRUPT_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CORRUPT_DEATH); + } + else + { + DoYell(SAY_CLEAN_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CLEAN_DEATH); + } + + if( pInstance ) + pInstance->SetData(DATA_HYDROSSTHEUNSTABLEEVENT, NOT_STARTED); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + // corrupted form + if( CorruptedForm ) + { + //MarkOfCorruption_Timer + if( MarkOfCorruption_Timer < diff ) + { + if( MarkOfCorruption_Count <= 5 ) + { + uint32 mark_spell; + + switch(MarkOfCorruption_Count) + { + case 0: mark_spell = SPELL_MARK_OF_CORRUPTION1; break; + case 1: mark_spell = SPELL_MARK_OF_CORRUPTION2; break; + case 2: mark_spell = SPELL_MARK_OF_CORRUPTION3; break; + case 3: mark_spell = SPELL_MARK_OF_CORRUPTION4; break; + case 4: mark_spell = SPELL_MARK_OF_CORRUPTION5; break; + case 5: mark_spell = SPELL_MARK_OF_CORRUPTION6; break; + } + + DoCast(m_creature->getVictim(), mark_spell); + + if( MarkOfCorruption_Count < 5 ) + MarkOfCorruption_Count++; + } + + MarkOfCorruption_Timer = 15000; + }else MarkOfCorruption_Timer -= diff; + + //VileSludge_Timer + if( VileSludge_Timer < diff ) + { + Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if( target ) + DoCast(target, SPELL_VILE_SLUDGE); + + VileSludge_Timer = 15000; + }else VileSludge_Timer -= diff; + + //PosCheck_Timer + if( PosCheck_Timer < diff ) + { + if( m_creature->GetDistance2d(HYDROSS_X, HYDROSS_Y) < SWITCH_RADIUS ) + { + // switch to clean form + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, MODEL_CLEAN); + CorruptedForm = false; + MarkOfHydross_Count = 0; + + DoYell(SAY_SWITCH_TO_CLEAN, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SWITCH_TO_CLEAN); + DoResetThreat(); + + // spawn 4 adds + DoSpawnCreature(ENTRY_PURE_SPAWN, SPAWN_X_DIFF1, SPAWN_Y_DIFF1, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + DoSpawnCreature(ENTRY_PURE_SPAWN, SPAWN_X_DIFF2, SPAWN_Y_DIFF2, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + DoSpawnCreature(ENTRY_PURE_SPAWN, SPAWN_X_DIFF3, SPAWN_Y_DIFF3, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + DoSpawnCreature(ENTRY_PURE_SPAWN, SPAWN_X_DIFF4, SPAWN_Y_DIFF4, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + + m_creature->SetMeleeDamageSchool(SPELL_SCHOOL_FROST); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + } + + PosCheck_Timer = 2500; + }else PosCheck_Timer -=diff; + } + // clean form + else + { + //MarkOfHydross_Timer + if( MarkOfHydross_Timer < diff ) + { + if( MarkOfHydross_Count <= 5 ) + { + uint32 mark_spell; + + switch(MarkOfHydross_Count) + { + case 0: mark_spell = SPELL_MARK_OF_HYDROSS1; break; + case 1: mark_spell = SPELL_MARK_OF_HYDROSS2; break; + case 2: mark_spell = SPELL_MARK_OF_HYDROSS3; break; + case 3: mark_spell = SPELL_MARK_OF_HYDROSS4; break; + case 4: mark_spell = SPELL_MARK_OF_HYDROSS5; break; + case 5: mark_spell = SPELL_MARK_OF_HYDROSS6; break; + } + + DoCast(m_creature->getVictim(), mark_spell); + + if( MarkOfHydross_Count < 5 ) + MarkOfHydross_Count++; + } + + MarkOfHydross_Timer = 15000; + }else MarkOfHydross_Timer -= diff; + + //WaterTomb_Timer + if( WaterTomb_Timer < diff ) + { + Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if( target ) + DoCast(target, SPELL_WATER_TOMB); + + WaterTomb_Timer = 7000; + }else WaterTomb_Timer -= diff; + + //PosCheck_Timer + if( PosCheck_Timer < diff ) + { + if( m_creature->GetDistance2d(HYDROSS_X, HYDROSS_Y) >= SWITCH_RADIUS ) + { + // switch to corrupted form + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, MODEL_CORRUPT); + MarkOfCorruption_Count = 0; + CorruptedForm = true; + + DoYell(SAY_SWITCH_TO_CORRUPT, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SWITCH_TO_CORRUPT); + DoResetThreat(); + + // spawn 4 adds + DoSpawnCreature(ENTRY_TAINTED_SPAWN, SPAWN_X_DIFF1, SPAWN_Y_DIFF1, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + DoSpawnCreature(ENTRY_TAINTED_SPAWN, SPAWN_X_DIFF2, SPAWN_Y_DIFF2, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + DoSpawnCreature(ENTRY_TAINTED_SPAWN, SPAWN_X_DIFF3, SPAWN_Y_DIFF3, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + DoSpawnCreature(ENTRY_TAINTED_SPAWN, SPAWN_X_DIFF4, SPAWN_Y_DIFF4, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + + m_creature->SetMeleeDamageSchool(SPELL_SCHOOL_NATURE); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + } + + PosCheck_Timer = 2500; + }else PosCheck_Timer -=diff; + } + + //EnrageTimer + if( EnrageTimer < diff ) + { + DoCast(m_creature, SPELL_ENRAGE); + EnrageTimer = 60000; + }else EnrageTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_hydross_the_unstable(Creature *_Creature) +{ + return new boss_hydross_the_unstableAI (_Creature); +} + +void AddSC_boss_hydross_the_unstable() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_hydross_the_unstable"; + newscript->GetAI = GetAI_boss_hydross_the_unstable; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp new file mode 100644 index 00000000000..b078c332251 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp @@ -0,0 +1,940 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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, MA02111-1307USA + */ + +/* ScriptData +SDName: Boss_Lady_Vashj +SD%Complete: 99 +SDComment: Missing blizzlike Shield Generators coords +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "def_serpent_shrine.h" +#include "../../../creature/simple_ai.h" +#include "Item.h" +#include "Spell.h" + +#define SPELL_MULTI_SHOT 38310 +#define SPELL_SHOCK_BLAST 38509 +#define SPELL_ENTANGLE 38316 +#define SPELL_STATIC_CHARGE_TRIGGER 38280 +#define SPELL_FORKED_LIGHTNING 40088 +#define SPELL_SHOOT 40873 +#define SPELL_POISON_BOLT 40095 +#define SPELL_TOXIC_SPORES 38575 +#define SPELL_MAGIC_BARRIER 38112 + +#define SAY_INTRO "Water is life. It has become a rare commodity here in Outland. A commodity that we alone shall control. We are the Highborne, and the time has come at last for us to retake our rightful place in the world!" +#define SAY_AGGRO1 "I'll split you from stem to stern! " +#define SAY_AGGRO2 "Victory to Lord Illidan!" +#define SAY_AGGRO3 "I spit on you, surface filth!" +#define SAY_AGGRO4 "Death to the outsiders!" +#define SAY_PHASE1 "I did not wish to lower myself by engaging your kind, but you leave me little choice!" +#define SAY_PHASE2 "The time is now! Leave none standing!" +#define SAY_PHASE3 "You may want to take cover." +#define SAY_BOWSHOT1 "Straight to the heart!" +#define SAY_BOWSHOT2 "Seek your mark!" +#define SAY_SLAY1 "Your time ends now!" +#define SAY_SLAY2 "You have failed!" +#define SAY_DEATH "Lord Illidan, I... I am... sorry." + +#define SOUND_INTRO 11531 +#define SOUND_AGGRO1 11532 +#define SOUND_AGGRO2 11533 +#define SOUND_AGGRO3 11534 +#define SOUND_AGGRO4 11535 +#define SOUND_PHASE1 11538 +#define SOUND_PHASE2 11539 +#define SOUND_PHASE3 11540 +#define SOUND_BOWSHOT1 11536 +#define SOUND_BOWSHOT2 11537 +#define SOUND_SLAY1 11541 +#define SOUND_SLAY2 11542 +#define SOUND_DEATH 11544 + +#define MIDDLE_X 30.134 +#define MIDDLE_Y -923.65 +#define MIDDLE_Z 42.9 + +#define SPOREBAT_X 30.977156 +#define SPOREBAT_Y -925.297761 +#define SPOREBAT_Z 77.176567 +#define SPOREBAT_O 5.223932 + +#define SHIED_GENERATOR_CHANNEL 19870 +#define ENCHANTED_ELEMENTAL 21958 +#define TAINTED_ELEMENTAL 22009 +#define COILFANG_STRIDER 22056 +#define COILFANG_ELITE 22055 +#define FATHOM_SPOREBAT 22140 + +float ElementPos[8][4] = +{ + {8.3, -835.3, 21.9, 5}, + {53.4, -835.3, 21.9, 4.5}, + {96, -861.9, 21.8, 4}, + {96, -986.4, 21.4, 2.5}, + {54.4, -1010.6, 22, 1.8}, + {9.8, -1012, 21.7, 1.4}, + {-35, -987.6, 21.5, 0.8}, + {-58.9, -901.6, 21.5, 6} +}; + +float CoilfangElitePos[3][4] = +{ + {28.84, -923.28, 42.9, 6}, + {31.183281, -953.502625, 41.523602, 1.640957}, + {58.895180, -923.124268, 41.545307, 3.152848} +}; + +float CoilfangStriderPos[3][4] = +{ + {66.427010, -948.778503, 41.262245, 2.584220}, + {7.513962, -959.538208, 41.300422, 1.034629}, + {-12.843201, -907.798401, 41.239620, 6.087094} +}; + +float ShieldGeneratorChannelPos[4][4] = +{ + {49.6262, -902.181, 43.0975, 3.95683}, + {10.988, -901.616, 42.5371, 5.4373}, + {10.3859, -944.036, 42.5446, 0.779888}, + {49.3126, -943.398, 42.5501, 2.40174} +}; + +//Lady Vashj AI +struct MANGOS_DLL_DECL boss_lady_vashjAI : public ScriptedAI +{ + boss_lady_vashjAI (Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint64 ShieldGeneratorChannel[4]; + + uint32 ShockBlast_Timer; + uint32 Entangle_Timer; + uint32 StaticCharge_Timer; + uint32 ForkedLightning_Timer; + uint32 Check_Timer; + uint32 EnchantedElemental_Timer; + uint32 TaintedElemental_Timer; + uint32 CoilfangElite_Timer; + uint32 CoilfangStrider_Timer; + uint32 SummonSporebat_Timer; + uint32 SummonSporebat_StaticTimer; + uint8 EnchantedElemental_Pos; + uint8 Phase; + + bool Entangle; + + void Reset() + { + ShockBlast_Timer = 1+rand()%60000; + Entangle_Timer = 30000; + StaticCharge_Timer = 10000+rand()%15000; + ForkedLightning_Timer = 2000; + Check_Timer = 1000; + EnchantedElemental_Timer = 5000; + TaintedElemental_Timer = 50000; + CoilfangElite_Timer = 45000+rand()%5000; + CoilfangStrider_Timer = 60000+rand()%10000; + SummonSporebat_Timer = 10000; + SummonSporebat_StaticTimer = 30000; + EnchantedElemental_Pos = 0; + Phase = 0; + + Entangle = false; + + if(pInstance) + pInstance->SetData(DATA_LADYVASHJEVENT, 0); + + ShieldGeneratorChannel[0] = 0; + ShieldGeneratorChannel[1] = 0; + ShieldGeneratorChannel[2] = 0; + ShieldGeneratorChannel[3] = 0; + + } + + //Called when a tainted elemental dies + void EventTaintedElementalDeath() + { + //the next will spawn 50 seconds after the previous one's death + if(TaintedElemental_Timer > 50000) + TaintedElemental_Timer = 50000; + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + + case 1: + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + DoYell(SAY_SLAY2, LANG_UNIVERSAL, NULL); + break; + } + } + + void JustDied(Unit *victim) + { + DoPlaySoundToSet(m_creature, SOUND_DEATH); + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + + if(pInstance) + pInstance->SetData(DATA_LADYVASHJEVENT, 0); + } + + void StartEvent() + { + switch(rand()%4) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + DoYell(SAY_AGGRO1, LANG_UNIVERSAL, NULL); + break; + case 1: + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + DoYell(SAY_AGGRO2, LANG_UNIVERSAL, NULL); + break; + case 2: + DoPlaySoundToSet(m_creature, SOUND_AGGRO3); + DoYell(SAY_AGGRO3, LANG_UNIVERSAL, NULL); + break; + case 3: + DoPlaySoundToSet(m_creature, SOUND_AGGRO4); + DoYell(SAY_AGGRO4, LANG_UNIVERSAL, NULL); + break; + } + + Phase = 1; + + if(pInstance) + pInstance->SetData(DATA_LADYVASHJEVENT, 1); + } + + void Aggro(Unit *who) + { + //Begin melee attack if we are within range + if(Phase != 2) + DoStartAttackAndMovement(who); + + StartEvent(); + } + + void CastShootOrMultishot() + { + switch(rand()%2) + { + case 0: + //Shoot + //Used in Phases 1 and 3 after Entangle or while having nobody in melee range. A shot that hits her target for 4097-5543 Physical damage. + DoCast(m_creature->getVictim(), SPELL_SHOOT); + break; + case 1: + //Multishot + //Used in Phases 1 and 3 after Entangle or while having nobody in melee range. A shot that hits 1 person and 4 people around him for 6475-7525 physical damage. + DoCast(m_creature->getVictim(), SPELL_MULTI_SHOT); + break; + } + + if(rand()%3) + { + switch(rand()%2) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_BOWSHOT1); + DoYell(SAY_BOWSHOT1, LANG_UNIVERSAL, NULL); + break; + case 1: + DoPlaySoundToSet(m_creature, SOUND_BOWSHOT2); + DoYell(SAY_BOWSHOT2, LANG_UNIVERSAL, NULL); + break; + } + } + } + + void UpdateAI(const uint32 diff) + { + //to prevent abuses during phase 2 + if(Phase == 2 && !m_creature->getVictim() && InCombat) + EnterEvadeMode(); + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(Phase == 1 || Phase == 3) + { + //ShockBlast_Timer + if (ShockBlast_Timer < diff) + { + //Shock Burst + //Randomly used in Phases 1 and 3 on Vashj's target, it's a Shock spell doing 8325-9675 nature damage and stunning the target for 5 seconds, during which she will not attack her target but switch to the next person on the aggro list. + DoCast(m_creature->getVictim(), SPELL_SHOCK_BLAST); + m_creature->TauntApply(m_creature->getVictim()); + + ShockBlast_Timer = 1000+rand()%14000; //random cooldown + }else ShockBlast_Timer -= diff; + + //StaticCharge_Timer + if(StaticCharge_Timer < diff) + { + //Static Charge + //Used on random people (only 1 person at any given time) in Phases 1 and 3, it's a debuff doing 2775 to 3225 Nature damage to the target and everybody in about 5 yards around it, every 1 seconds for 30 seconds. It can be removed by Cloak of Shadows, Iceblock, Divine Shield, etc, but not by Cleanse or Dispel Magic. + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + if(target && !target->HasAura(SPELL_STATIC_CHARGE_TRIGGER, 0)) + //cast Static Charge every 2 seconds for 20 seconds + DoCast(target, SPELL_STATIC_CHARGE_TRIGGER); + + StaticCharge_Timer = 10000+rand()%20000; //blizzlike + }else StaticCharge_Timer -= diff; + + //Entangle_Timer + if (Entangle_Timer < diff) + { + if(!Entangle) + { + //Entangle + //Used in Phases 1 and 3, it casts Entangling Roots on everybody in a 15 yard radius of Vashj, immobilzing them for 10 seconds and dealing 500 damage every 2 seconds. It's not a magic effect so it cannot be dispelled, but is removed by various buffs such as Cloak of Shadows or Blessing of Freedom. + DoCast(m_creature->getVictim(), SPELL_ENTANGLE); + Entangle = true; + Entangle_Timer = 10000; + } + else + { + CastShootOrMultishot(); + Entangle = false; + Entangle_Timer = 20000+rand()%5000; + } + }else Entangle_Timer -= diff; + + //Phase 1 + if(Phase == 1) + { + //Start phase 2 + if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 70) + { + //Phase 2 begins when Vashj hits 70%. She will run to the middle of her platform and surround herself in a shield making her invulerable. + Phase = 2; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->GetMotionMaster()->Clear(); + m_creature->Relocate(MIDDLE_X, MIDDLE_Y, MIDDLE_Z); + m_creature->SendMonsterMove(MIDDLE_X, MIDDLE_Y, MIDDLE_Z, 0, 0, 0); + + m_creature->RemoveAllAuras(); + // This needs an entry in spell_script_target + DoCast(m_creature, SPELL_MAGIC_BARRIER, true); + + Creature *pCreature; + for(uint8 i = 0; i < 4; i++) + { + pCreature = m_creature->SummonCreature(SHIED_GENERATOR_CHANNEL, ShieldGeneratorChannelPos[i][0], ShieldGeneratorChannelPos[i][1], ShieldGeneratorChannelPos[i][2], ShieldGeneratorChannelPos[i][3], TEMPSUMMON_CORPSE_DESPAWN, 0); + if (pCreature) + ShieldGeneratorChannel[i] = pCreature->GetGUID(); + } + + DoPlaySoundToSet(m_creature, SOUND_PHASE2); + DoYell(SAY_PHASE2, LANG_UNIVERSAL, NULL); + } + } + //Phase 3 + else + { + //SummonSporebat_Timer + if(SummonSporebat_Timer < diff) + { + Creature *Sporebat = NULL; + Sporebat = m_creature->SummonCreature(FATHOM_SPOREBAT, SPOREBAT_X, SPOREBAT_Y, SPOREBAT_Z, SPOREBAT_O, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + + if(Sporebat) + { + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + Sporebat->AI()->AttackStart(target); + } + + //summon sporebats faster and faster + if(SummonSporebat_StaticTimer > 1000) + SummonSporebat_StaticTimer -= 1000; + + SummonSporebat_Timer = SummonSporebat_StaticTimer; + }else SummonSporebat_Timer -= diff; + } + + //Melee attack + DoMeleeAttackIfReady(); + + //Check_Timer - used to check if somebody is in melee range + if(Check_Timer < diff) + { + bool InMeleeRange = false; + Unit *target; + std::list t_list = m_creature->getThreatManager().getThreatList(); + for(std::list::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr) + { + target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + //if in melee range + if(target && target->IsWithinDistInMap(m_creature, 5)) + { + InMeleeRange = true; + break; + } + } + + //if nobody is in melee range + if(!InMeleeRange) + CastShootOrMultishot(); + + Check_Timer = 1000; + }else Check_Timer -= diff; + } + //Phase 2 + else + { + //ForkedLightning_Timer + if(ForkedLightning_Timer < diff) + { + //Forked Lightning + //Used constantly in Phase 2, it shoots out completely randomly targeted bolts of lightning which hit everybody in a roughtly 60 degree cone in front of Vashj for 2313-2687 nature damage. + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + if(!target) + target = m_creature->getVictim(); + + DoCast(target, SPELL_FORKED_LIGHTNING); + + ForkedLightning_Timer = 2000+rand()%6000; //blizzlike + }else ForkedLightning_Timer -= diff; + + //EnchantedElemental_Timer + if(EnchantedElemental_Timer < diff) + { + Creature *Elemental; + Elemental = m_creature->SummonCreature(ENCHANTED_ELEMENTAL, ElementPos[EnchantedElemental_Pos][0], ElementPos[EnchantedElemental_Pos][1], ElementPos[EnchantedElemental_Pos][2], ElementPos[EnchantedElemental_Pos][3], TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); + if(Elemental) + Elemental->GetMotionMaster()->MovePoint(0, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()); + + if(EnchantedElemental_Pos == 7) + EnchantedElemental_Pos = 0; + else + EnchantedElemental_Pos++; + + EnchantedElemental_Timer = 10000+rand()%5000; + }else EnchantedElemental_Timer -= diff; + + //TaintedElemental_Timer + if(TaintedElemental_Timer < diff) + { + Creature *Tain_Elemental; + uint32 pos = rand()%8; + Tain_Elemental = m_creature->SummonCreature(TAINTED_ELEMENTAL, ElementPos[pos][0], ElementPos[pos][1], ElementPos[pos][2], ElementPos[pos][3], TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000); + if(Tain_Elemental) + { + Tain_Elemental->GetMotionMaster()->Clear(); + Tain_Elemental->GetMotionMaster()->MoveIdle(); + } + + TaintedElemental_Timer = 120000; + }else TaintedElemental_Timer -= diff; + + //CoilfangElite_Timer + if(CoilfangElite_Timer < diff) + { + Creature *CoilfangElite; + uint32 pos = rand()%3; + CoilfangElite = m_creature->SummonCreature(COILFANG_ELITE, CoilfangElitePos[pos][0], CoilfangElitePos[pos][1], CoilfangElitePos[pos][2], CoilfangElitePos[pos][3], TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 45000); + if(CoilfangElite) + { + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + CoilfangElite->AI()->AttackStart(target); + } + + CoilfangElite_Timer = 45000+rand()%5000; //wowwiki says 50 seconds, bosskillers says 45 + }else CoilfangElite_Timer -= diff; + + //CoilfangStrider_Timer + if(CoilfangStrider_Timer < diff) + { + Creature *CoilfangStrider; + uint32 pos = rand()%3; + CoilfangStrider = m_creature->SummonCreature(COILFANG_STRIDER, CoilfangStriderPos[pos][0], CoilfangStriderPos[pos][1], CoilfangStriderPos[pos][2], CoilfangStriderPos[pos][3], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + if(CoilfangStrider) + { + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + CoilfangStrider->AI()->AttackStart(target); + } + + CoilfangStrider_Timer = 60000+rand()%10000; //wowwiki says 60 seconds, bosskillers says 60-70 + }else CoilfangStrider_Timer -= diff; + + //Check_Timer + if(Check_Timer < diff) + { + //Start Phase 3 + if(pInstance && pInstance->GetData(DATA_CANSTARTPHASE3)) + { + //set life 50% + m_creature->SetHealth(m_creature->GetMaxHealth()/2); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->RemoveAurasDueToSpell(SPELL_MAGIC_BARRIER); + + DoPlaySoundToSet(m_creature, SOUND_PHASE3); + DoYell(SAY_PHASE3, LANG_UNIVERSAL, NULL); + + Phase = 3; + + //return to the tank + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + } + Check_Timer = 1000; + }else Check_Timer -= diff; + } + } +}; + +//Enchanted Elemental +//If one of them reaches Vashj he will increase her damage done by 5%. +struct MANGOS_DLL_DECL mob_enchanted_elementalAI : public ScriptedAI +{ + mob_enchanted_elementalAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 Check_Timer; + uint32 Movement_Timer; + + void Reset() + { + Check_Timer = 5000; + Movement_Timer = 500; + } + + void Aggro(Unit *who) { return; } + + void MoveInLineOfSight(Unit *who) { return; } + + void UpdateAI(const uint32 diff) + { + //Check_Timer + if(Check_Timer < diff) + { + if(pInstance) + { + Unit *Vashj = NULL; + Vashj = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_LADYVASHJ)); + if(Vashj) + { + if(Vashj->IsWithinDistInMap(m_creature, 5)) + { + //increase lady vashj damage (+5%) + const CreatureInfo *cinfo = m_creature->GetCreatureInfo(); + Vashj->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 5))); + Vashj->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 5))); + m_creature->UpdateDamagePhysical(BASE_ATTACK); + + //call Unsummon() + m_creature->setDeathState(JUST_DIED); + } + else if(((boss_lady_vashjAI*)((Creature*)Vashj)->AI())->InCombat == false) + { + //call Unsummon() + m_creature->setDeathState(JUST_DIED); + } + } + } + else error_log("ERROR: Instance Data for Serpentshrine Caverns not set"); + Check_Timer = 1000; + }else Check_Timer -= diff; + } +}; + +//Tainted Elemental +//This mob has 7,900 life, doesn't move, and shoots Poison Bolts at one person anywhere in the area, doing 3,000 nature damage and placing a posion doing 2,000 damage every 2 seconds. He will switch targets often, or sometimes just hang on a single player, but there is nothing you can do about it except heal the damage and kill the Tainted Elemental +struct MANGOS_DLL_DECL mob_tainted_elementalAI : public ScriptedAI +{ + mob_tainted_elementalAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 PoisonBolt_Timer; + + void Reset() + { + PoisonBolt_Timer = 5000+rand()%5000; + } + + void JustDied(Unit *killer) + { + if(pInstance) + { + Creature *Vashj = NULL; + Vashj = (Creature*)(Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_LADYVASHJ))); + + if(Vashj) + ((boss_lady_vashjAI*)Vashj->AI())->EventTaintedElementalDeath(); + } + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + m_creature->AddThreat(who, 0.1f); + } + } + } + + void UpdateAI(const uint32 diff) + { + //PoisonBolt_Timer + if(PoisonBolt_Timer < diff) + { + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + if(target && target->IsWithinDistInMap(m_creature, 30)) + DoCast(target, SPELL_POISON_BOLT); + + PoisonBolt_Timer = 5000+rand()%5000; + }else PoisonBolt_Timer -= diff; + } +}; + +//Fathom Sporebat +//Toxic Spores: Used in Phase 3 by the Spore Bats, it creates a contaminated green patch of ground, dealing about 2775-3225 nature damage every second to anyone who stands in it. +struct MANGOS_DLL_DECL mob_fathom_sporebatAI : public ScriptedAI +{ + mob_fathom_sporebatAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 ToxicSpore_Timer; + uint32 Check_Timer; + + void Reset() + { + m_creature->setFaction(14); + ToxicSpore_Timer = 5000; + Check_Timer = 1000; + } + + void Aggro(Unit *who) {} + + void UpdateAI (const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ToxicSpore_Timer + if(ToxicSpore_Timer < diff) + { + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + //The Spores will hit you anywhere in the instance: underwater, at the elevator, at the entrance, wherever. + if(target) + DoCast(target, SPELL_TOXIC_SPORES); + + ToxicSpore_Timer = 20000+rand()%5000; + }else ToxicSpore_Timer -= diff; + + //Check_Timer + if(Check_Timer < diff) + { + if(pInstance) + { + //check if vashj is death + Unit *Vashj = NULL; + Vashj = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_LADYVASHJ)); + if(!Vashj || (Vashj && !Vashj->isAlive())) + { + //remove + m_creature->setDeathState(DEAD); + m_creature->RemoveCorpse(); + m_creature->setFaction(35); + } + } + + Check_Timer = 1000; + }else Check_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Coilfang Elite +//It's an elite Naga mob with 170,000 HP. It does about 5000 damage on plate, and has a nasty cleave hitting for about 7500 damage +CreatureAI* GetAI_mob_coilfang_elite(Creature *_Creature) +{ + SimpleAI* ai = new SimpleAI (_Creature); + + ai->Spell[0].Enabled = true; + ai->Spell[0].Spell_Id = 31345; //Cleave + ai->Spell[0].Cooldown = 15000; + ai->Spell[0].CooldownRandomAddition = 5000; + ai->Spell[0].First_Cast = 5000; + ai->Spell[0].Cast_Target_Type = CAST_HOSTILE_RANDOM; + + ai->EnterEvadeMode(); + + return ai; +} + +//Coilfang Strifer +//It hits plate for about 8000 damage, has a Mind Blast spell doing about 3000 shadow damage, and a Psychic Scream Aura, which fears everybody in a 8 yard range of it every 2-3 seconds , for 5 seconds and increasing their movement speed by 150% during the fear. +CreatureAI* GetAI_mob_coilfang_strider(Creature *_Creature) +{ + SimpleAI* ai = new SimpleAI (_Creature); + + ai->Spell[0].Enabled = true; + ai->Spell[0].Spell_Id = 41374; //Mind Blast + ai->Spell[0].Cooldown = 30000; + ai->Spell[0].CooldownRandomAddition = 10000; + ai->Spell[0].First_Cast = 8000; + ai->Spell[0].Cast_Target_Type = CAST_HOSTILE_TARGET; + + //Scream aura not implemented + + ai->EnterEvadeMode(); + + return ai; +} + +struct MANGOS_DLL_DECL mob_shield_generator_channelAI : public ScriptedAI +{ + mob_shield_generator_channelAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 Check_Timer; + bool Channeled; + + void Reset() + { + Check_Timer = 1000; + Channeled = false; + //invisible + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID , 11686); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit *who) { return; } + + void MoveInLineOfSight(Unit *who) { return; } + + void UpdateAI (const uint32 diff) + { + if(!pInstance) + return; + + if(!Channeled) + { + Unit *Vashj = NULL; + Vashj = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_LADYVASHJ)); + + if(Vashj && Vashj->isAlive()) + { + //start visual channel + m_creature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, Vashj->GetGUID()); + m_creature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_MAGIC_BARRIER); + Channeled = true; + } + } + } +}; + +bool ItemUse_item_tainted_core(Player *player, Item* _Item, SpellCastTargets const& targets) +{ + ScriptedInstance *pInstance = ((ScriptedInstance*)player->GetInstanceData()); + + if(!pInstance) + { + player->GetSession()->SendNotification("ERROR: Instance script not initialized. Notify your administrator."); + error_log("ERROR: Lady Vashj Tainted Core: Instance Script Not Initialized"); + return true; + } + + Creature *Vashj = NULL; + Vashj = (Creature*)(Unit::GetUnit((*player), pInstance->GetData64(DATA_LADYVASHJ))); + if(Vashj && ((boss_lady_vashjAI*)Vashj->AI())->Phase == 2) + { + if(targets.getGOTarget() && targets.getGOTarget()->GetTypeId()==TYPEID_GAMEOBJECT) + { + uint32 identifier; + uint8 channel_identifier; + switch(targets.getGOTarget()->GetEntry()) + { + case 185052: + identifier = DATA_SHIELDGENERATOR1; + channel_identifier = 0; + break; + case 185053: + identifier = DATA_SHIELDGENERATOR2; + channel_identifier = 1; + break; + case 185051: + identifier = DATA_SHIELDGENERATOR3; + channel_identifier = 2; + break; + case 185054: + identifier = DATA_SHIELDGENERATOR4; + channel_identifier = 3; + break; + default: + return true; + break; + } + + if(pInstance->GetData(identifier)) + { + player->GetSession()->SendNotification("Already deactivated"); + return true; + } + + //get and remove channel + Unit *Channel = NULL; + Channel = Unit::GetUnit((*Vashj), ((boss_lady_vashjAI*)Vashj->AI())->ShieldGeneratorChannel[channel_identifier]); + if(Channel) + { + //call Unsummon() + Channel->setDeathState(JUST_DIED); + } + + pInstance->SetData(identifier, 1); + + //remove this item + player->DestroyItemCount(31088, 1, true); + } + } + return true; +} + +CreatureAI* GetAI_boss_lady_vashj(Creature *_Creature) +{ + return new boss_lady_vashjAI (_Creature); +} + +CreatureAI* GetAI_mob_enchanted_elemental(Creature *_Creature) +{ + return new mob_enchanted_elementalAI (_Creature); +} + +CreatureAI* GetAI_mob_tainted_elemental(Creature *_Creature) +{ + return new mob_tainted_elementalAI (_Creature); +} + +CreatureAI* GetAI_mob_fathom_sporebat(Creature *_Creature) +{ + return new mob_fathom_sporebatAI (_Creature); +} + +CreatureAI* GetAI_mob_shield_generator_channel(Creature *_Creature) +{ + return new mob_shield_generator_channelAI (_Creature); +} + +void AddSC_boss_lady_vashj() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_lady_vashj"; + newscript->GetAI = GetAI_boss_lady_vashj; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_enchanted_elemental"; + newscript->GetAI = GetAI_mob_enchanted_elemental; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_tainted_elemental"; + newscript->GetAI = GetAI_mob_tainted_elemental; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_fathom_sporebat"; + newscript->GetAI = GetAI_mob_fathom_sporebat; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_coilfang_elite"; + newscript->GetAI = GetAI_mob_coilfang_elite; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_coilfang_strider"; + newscript->GetAI = GetAI_mob_coilfang_strider; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_shield_generator_channel"; + newscript->GetAI = GetAI_mob_shield_generator_channel; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="item_tainted_core"; + newscript->pItemUse = ItemUse_item_tainted_core; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp new file mode 100644 index 00000000000..ea54e5ffcab --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp @@ -0,0 +1,359 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Leotheras_The_Blind +SD%Complete: 50 +SDComment: Missing Inner Demons +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "def_serpent_shrine.h" + +#define SPELL_WHIRLWIND 40653 +#define SPELL_CHAOS_BLAST 37675 +//#define SPELL_INSIDIOUS_WHISPER 37676 // useless - dummy effect that can't be implemented + +#define SAY_AGGRO "Finally my banishment ends!" +#define SAY_SWITCH_TO_DEMON "Be gone trifling elf. I'm in control now." +#define SAY_INNER_DEMONS "We all have our demons..." +#define SAY_DEMON_SLAY1 "I have no equal." +#define SAY_DEMON_SLAY2 "Perish, mortal." +#define SAY_DEMON_SLAY3 "Yes, YES! Ahahah!" +#define SAY_NIGHTELF_SLAY1 "Kill! KILL!" +#define SAY_NIGHTELF_SLAY2 "That's right! Yes!" +#define SAY_NIGHTELF_SLAY3 "Who's the master now?" +#define SAY_FINAL_FORM "No! NO! What have you done?! I am the master, do you hear me? I... aaghh... Can't... contain him..." +#define SAY_FREE "At last I am liberated. It has been too long since I have tasted true freedom!" +#define SAY_DEATH "You cannot kill me! Fools, I'll be back! I'll... aarghh..." + +#define SOUND_AGGRO 11312 +#define SOUND_SWITCH_TO_DEMON 11304 +#define SOUND_INNER_DEMONS 11305 +#define SOUND_DEMON_SLAY1 11306 +#define SOUND_DEMON_SLAY2 11307 +#define SOUND_DEMON_SLAY3 11308 +#define SOUND_NIGHTELF_SLAY1 11314 +#define SOUND_NIGHTELF_SLAY2 11315 +#define SOUND_NIGHTELF_SLAY3 11316 +#define SOUND_FINAL_FORM 11313 +#define SOUND_FREE 11309 +#define SOUND_DEATH 11317 + +#define MODEL_DEMON 14555 +#define MODEL_NIGHTELF 20514 + +#define DEMON_FORM 21845 + +//Original Leotheras the Blind AI +struct MANGOS_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI +{ + boss_leotheras_the_blindAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Demon = 0; + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 Whirlwind_Timer; + uint32 ChaosBlast_Timer; + uint32 Switch_Timer; + + bool DemonForm; + bool IsFinalForm; + + uint64 Demon; + + void Reset() + { + Whirlwind_Timer = 20000; + ChaosBlast_Timer = 1000; + Switch_Timer = 45000; + + DemonForm = false; + IsFinalForm = false; + + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, MODEL_NIGHTELF); + + if(pInstance) + pInstance->SetData(DATA_LEOTHERASTHEBLINDEVENT, 0); + } + + void StartEvent() + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + + if(pInstance) + pInstance->SetData(DATA_LEOTHERASTHEBLINDEVENT, 1); + } + + void KilledUnit(Unit *victim) + { + if(victim->GetTypeId() != TYPEID_PLAYER) + return; + + if(DemonForm) + switch(rand()%3) + { + case 0: + DoYell(SAY_DEMON_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEMON_SLAY1); + break; + case 1: + DoYell(SAY_DEMON_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEMON_SLAY2); + break; + case 2: + DoYell(SAY_DEMON_SLAY3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEMON_SLAY3); + break; + } + else + switch(rand()%3) + { + case 0: + DoYell(SAY_NIGHTELF_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_NIGHTELF_SLAY1); + break; + case 1: + DoYell(SAY_NIGHTELF_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_NIGHTELF_SLAY2); + break; + case 2: + DoYell(SAY_NIGHTELF_SLAY3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_NIGHTELF_SLAY3); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + //despawn copy + if(Demon) + { + Unit *pUnit = NULL; + pUnit = Unit::GetUnit((*m_creature), Demon); + + if(pUnit) + pUnit->DealDamage(pUnit, pUnit->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + if(pInstance) + pInstance->SetData(DATA_LEOTHERASTHEBLINDEVENT, 0); + } + + void Aggro(Unit *who) + { + StartEvent(); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(!DemonForm) + { + //Whirlwind_Timer + if(Whirlwind_Timer < diff) + { + DoCast(m_creature, SPELL_WHIRLWIND); + Whirlwind_Timer = 25000; + }else Whirlwind_Timer -= diff; + + //Switch_Timer + if(!IsFinalForm) + if(Switch_Timer < diff) + { + //switch to demon form + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, MODEL_DEMON); + DoYell(SAY_SWITCH_TO_DEMON, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SWITCH_TO_DEMON); + DemonForm = true; + + Switch_Timer = 60000; + }else Switch_Timer -= diff; + + DoMeleeAttackIfReady(); + } + else + { + //ChaosBlast_Timer + if(ChaosBlast_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CHAOS_BLAST); + ChaosBlast_Timer = 1500; + }else ChaosBlast_Timer -= diff; + + //Switch_Timer + if(Switch_Timer < diff) + { + //switch to nightelf form + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, MODEL_NIGHTELF); + DemonForm = false; + + Switch_Timer = 45000; + }else Switch_Timer -= diff; + } + + if(!IsFinalForm && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 15) + { + //at this point he divides himself in two parts + Creature *Copy = NULL; + Copy = DoSpawnCreature(DEMON_FORM, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + + if(Copy) + { + Demon = Copy->GetGUID(); + Copy->AI()->AttackStart(m_creature->getVictim()); + } + + //set nightelf final form + IsFinalForm = true; + DemonForm = false; + + DoYell(SAY_FINAL_FORM, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_FINAL_FORM); + + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, MODEL_NIGHTELF); + } + } +}; + +//Leotheras the Blind Demon Form AI +struct MANGOS_DLL_DECL boss_leotheras_the_blind_demonformAI : public ScriptedAI +{ + boss_leotheras_the_blind_demonformAI(Creature *c) : ScriptedAI(c) + { + Reset(); + } + + uint32 ChaosBlast_Timer; + + void Reset() + { + ChaosBlast_Timer = 1000; + } + + void StartEvent() + { + DoYell(SAY_FREE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_FREE); + } + + void KilledUnit(Unit *victim) + { + if(victim->GetTypeId() != TYPEID_PLAYER) + return; + + switch(rand()%3) + { + case 0: + DoYell(SAY_DEMON_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEMON_SLAY1); + break; + case 1: + DoYell(SAY_DEMON_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEMON_SLAY2); + break; + case 2: + DoYell(SAY_DEMON_SLAY3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEMON_SLAY3); + break; + } + } + + void JustDied(Unit *victim) + { + //invisibility (blizzlike, at the end of the fight he doesn't die, he disappears) + m_creature->CastSpell(m_creature, 8149, true); + } + + void Aggro(Unit *who) + { + StartEvent(); + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + + if(!InCombat) + StartEvent(); + } + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ChaosBlast_Timer + if(ChaosBlast_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CHAOS_BLAST); + ChaosBlast_Timer = 1500; + }else ChaosBlast_Timer -= diff; + + //Do NOT deal any melee damage to the target. + } +}; + +CreatureAI* GetAI_boss_leotheras_the_blind(Creature *_Creature) +{ + return new boss_leotheras_the_blindAI (_Creature); +} + +CreatureAI* GetAI_boss_leotheras_the_blind_demonform(Creature *_Creature) +{ + return new boss_leotheras_the_blind_demonformAI (_Creature); +} + +void AddSC_boss_leotheras_the_blind() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_leotheras_the_blind"; + newscript->GetAI = GetAI_boss_leotheras_the_blind; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_leotheras_the_blind_demonform"; + newscript->GetAI = GetAI_boss_leotheras_the_blind_demonform; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp new file mode 100644 index 00000000000..27ebaf4bd7e --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp @@ -0,0 +1,406 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: Boss_Morogrim_Tidewalker +SD%Complete: 90 +SDComment: Water globules don't explode properly +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "def_serpent_shrine.h" + +#define SPELL_TIDAL_WAVE 37730 +#define SPELL_WATERY_GRAVE 38049 +#define SPELL_EARTHQUAKE 37764 +#define SPELL_WATERY_GRAVE_EXPLOSION 37852 + +#define SAY_AGGRO "Flood of the deep, take you!" +#define SAY_SUMMON1 "By the Tides, kill them at once!" +#define SAY_SUMMON2 "Destroy them my subjects!" +#define SAY_SLAY1 "It is done!" +#define SAY_SLAY2 "Strugging only makes it worse." +#define SAY_SLAY3 "Only the strong survive." +#define SAY_SUMMON_BUBL1 "There is nowhere to hide!" +#define SAY_SUMMON_BUBL2 "Soon it will be finished!" +#define SAY_DEATH "Great... currents of... Ageon." + +#define SOUND_AGGRO 11321 +#define SOUND_SUMMON1 11322 +#define SOUND_SUMMON2 11323 +#define SOUND_SLAY1 11326 +#define SOUND_SLAY2 11327 +#define SOUND_SLAY3 11328 +#define SOUND_SUMMON_BUBL1 11324 +#define SOUND_SUMMON_BUBL2 11325 +#define SOUND_DEATH 11329 + +#define WATERY_GRAVE_X1 334.64 +#define WATERY_GRAVE_Y1 -728.89 +#define WATERY_GRAVE_Z1 -14.42 +#define WATERY_GRAVE_X2 365.51 +#define WATERY_GRAVE_Y2 -737.14 +#define WATERY_GRAVE_Z2 -14.44 +#define WATERY_GRAVE_X3 366.19 +#define WATERY_GRAVE_Y3 -709.59 +#define WATERY_GRAVE_Z3 -14.36 +#define WATERY_GRAVE_X4 372.93 +#define WATERY_GRAVE_Y4 -690.96 +#define WATERY_GRAVE_Z4 -14.44 + +#define EMOTE_WATERY_GRAVE "sends his enemies to their watery graves!" +#define EMOTE_EARTHQUAKE "The violent earthquake has alerted nearby murlocs!" +#define EMOTE_WATERY_GLOBULES "summons Watery Globules!" + +#define WATER_GLOBULE 21913 +#define TIDEWALKER_LURKER 21920 + +//Morogrim Tidewalker AI +struct MANGOS_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI +{ + boss_morogrim_tidewalkerAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 TidalWave_Timer; + uint32 WateryGrave_Timer; + uint32 Earthquake_Timer; + uint32 WateryGlobules_Timer; + + bool Earthquake; + bool Phase2; + + void Reset() + { + TidalWave_Timer = 10000; + WateryGrave_Timer = 30000; + Earthquake_Timer = 40000; + WateryGlobules_Timer = 0; + + Earthquake = false; + Phase2 = false; + + if(pInstance) + pInstance->SetData(DATA_MOROGRIMTIDEWALKEREVENT, NOT_STARTED); + } + + void StartEvent() + { + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + + if(pInstance) + pInstance->SetData(DATA_MOROGRIMTIDEWALKEREVENT, IN_PROGRESS); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%3) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + DoYell(SAY_SLAY1, LANG_UNIVERSAL, NULL); + break; + + case 1: + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + DoYell(SAY_SLAY2, LANG_UNIVERSAL, NULL); + break; + + case 2: + DoPlaySoundToSet(m_creature, SOUND_SLAY3); + DoYell(SAY_SLAY3, LANG_UNIVERSAL, NULL); + break; + } + } + + void JustDied(Unit *victim) + { + DoPlaySoundToSet(m_creature, SOUND_DEATH); + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + + if(pInstance) + pInstance->SetData(DATA_MOROGRIMTIDEWALKEREVENT, NOT_STARTED); + } + + void Aggro(Unit *who) { StartEvent(); } + + void ApplyWateryGrave(Unit *player, uint8 pos) + { + float x, y, z; + + switch(pos) + { + case 0: + x = WATERY_GRAVE_X1; + y = WATERY_GRAVE_Y1; + z = WATERY_GRAVE_Z1; + break; + + case 1: + x = WATERY_GRAVE_X2; + y = WATERY_GRAVE_Y2; + z = WATERY_GRAVE_Z2; + break; + + case 2: + x = WATERY_GRAVE_X3; + y = WATERY_GRAVE_Y3; + z = WATERY_GRAVE_Z3; + break; + + case 3: + x = WATERY_GRAVE_X4; + y = WATERY_GRAVE_Y4; + z = WATERY_GRAVE_Z4; + break; + } + + DoTeleportPlayer(player, x, y, z+1, player->GetOrientation()); + DoCast(player, SPELL_WATERY_GRAVE); + } + + void SummonMurloc(float x, float y, float z) + { + Creature *Summoned; + + Summoned = m_creature->SummonCreature(TIDEWALKER_LURKER, x, y, z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + if(Summoned) + { + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + Summoned->AI()->AttackStart(target); + } + } + + void SummonWaterGlobule(float x, float y, float z) + { + Creature *Globule; + + Globule = m_creature->SummonCreature(WATER_GLOBULE, x, y, z, 0, TEMPSUMMON_TIMED_DESPAWN, 30000); //they despawn after 30 seconds + if(Globule) + { + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + Globule->AI()->AttackStart(target); + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Earthquake_Timer + if(Earthquake_Timer < diff) + { + if(!Earthquake) + { + DoCast(m_creature->getVictim(), SPELL_EARTHQUAKE); + Earthquake = true; + Earthquake_Timer = 10000; + } + else + { + switch(rand()%2) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_SUMMON1); + DoYell(SAY_SUMMON1, LANG_UNIVERSAL, NULL); + break; + + case 1: + DoPlaySoundToSet(m_creature, SOUND_SUMMON2); + DoYell(SAY_SUMMON2, LANG_UNIVERSAL, NULL); + break; + } + + //north + SummonMurloc(486.10, -723.64, -7.14); + SummonMurloc(482.58, -723.78, -7.14); + SummonMurloc(479.38, -723.91, -7.14); + SummonMurloc(476.03, -723.86, -7.14); + SummonMurloc(472.69, -723.69, -7.14); + SummonMurloc(469.04, -723.63, -7.14); + + //south + SummonMurloc(311.63, -725.04, -13.15); + SummonMurloc(307.81, -725.34, -13.15); + SummonMurloc(303.91, -725.64, -13.06); + SummonMurloc(300.23, -726, -11.89); + SummonMurloc(296.82, -726.33, -10.82); + SummonMurloc(293.64, -726.64, -9.81); + + DoTextEmote(EMOTE_EARTHQUAKE, NULL); + + Earthquake = false; + Earthquake_Timer = 40000+rand()%5000; + } + }else Earthquake_Timer -= diff; + + //TidalWave_Timer + if(TidalWave_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_TIDAL_WAVE); + TidalWave_Timer = 20000; + }else TidalWave_Timer -= diff; + + if(!Phase2) + { + //WateryGrave_Timer + if(WateryGrave_Timer < diff) + { + //Teleport 4 players under the waterfalls + Unit *target; + for(uint8 i = 0; i < 4; i++) + { + target = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(target && (target->GetTypeId() == TYPEID_PLAYER) && !target->HasAura(SPELL_WATERY_GRAVE, 0) && target->IsWithinDistInMap(m_creature, 50)) + ApplyWateryGrave(target, i); + } + + switch(rand()%2) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_SUMMON_BUBL1); + DoYell(SAY_SUMMON_BUBL1, LANG_UNIVERSAL, NULL); + break; + + case 1: + DoPlaySoundToSet(m_creature, SOUND_SUMMON_BUBL2); + DoYell(SAY_SUMMON_BUBL2, LANG_UNIVERSAL, NULL); + break; + + case 2: + break; + } + + DoTextEmote(EMOTE_WATERY_GRAVE, NULL); + + WateryGrave_Timer = 30000; + }else WateryGrave_Timer -= diff; + + //Start Phase2 + if((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 25) + Phase2 = true; + } + else + { + //WateryGlobules_Timer + if(WateryGlobules_Timer < diff) + { + SummonWaterGlobule(WATERY_GRAVE_X1, WATERY_GRAVE_Y1, WATERY_GRAVE_Z1); + SummonWaterGlobule(WATERY_GRAVE_X2, WATERY_GRAVE_Y2, WATERY_GRAVE_Z2); + SummonWaterGlobule(WATERY_GRAVE_X3, WATERY_GRAVE_Y3, WATERY_GRAVE_Z3); + SummonWaterGlobule(WATERY_GRAVE_X4, WATERY_GRAVE_Y4, WATERY_GRAVE_Z4); + + DoTextEmote(EMOTE_WATERY_GLOBULES, NULL); + + WateryGlobules_Timer = 25000; + }else WateryGlobules_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; + +//Water Globule AI +struct MANGOS_DLL_DECL mob_water_globuleAI : public ScriptedAI +{ + mob_water_globuleAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Check_Timer; + + void Reset() + { + Check_Timer = 1000; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->setFaction(14); + } + + void Aggro(Unit *who) {} + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + //no attack radius check - it attacks the first target that moves in his los + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(Check_Timer < diff) + { + if(m_creature->IsWithinDistInMap(m_creature->getVictim(), 5)) + { + uint32 damage = 4000+rand()%2000; + m_creature->DealDamage(m_creature->getVictim(), damage, NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_FROST, NULL, false); + + //despawn + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + Check_Timer = 500; + }else Check_Timer -= diff; + + //do NOT deal any melee damage to the target. + } +}; + +CreatureAI* GetAI_boss_morogrim_tidewalker(Creature *_Creature) +{ + return new boss_morogrim_tidewalkerAI (_Creature); +} +CreatureAI* GetAI_mob_water_globule(Creature *_Creature) +{ + return new mob_water_globuleAI (_Creature); +} + +void AddSC_boss_morogrim_tidewalker() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_morogrim_tidewalker"; + newscript->GetAI = GetAI_boss_morogrim_tidewalker; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_water_globule"; + newscript->GetAI = GetAI_mob_water_globule; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/def_serpent_shrine.h b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/def_serpent_shrine.h new file mode 100644 index 00000000000..21d76d6854a --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/def_serpent_shrine.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SERPENT_SHRINE_H +#define DEF_SERPENT_SHRINE_H + +#define DATA_CANSTARTPHASE3 1 +#define DATA_CARIBDIS 2 +#define DATA_HYDROSSTHEUNSTABLEEVENT 3 +#define DATA_KARATHRESS 4 +#define DATA_KARATHRESSEVENT 5 +#define DATA_KARATHRESSEVENT_STARTER 6 +#define DATA_LADYVASHJ 7 +#define DATA_LADYVASHJEVENT 8 +#define DATA_LEOTHERASTHEBLINDEVENT 9 +#define DATA_MOROGRIMTIDEWALKEREVENT 10 +#define DATA_SHARKKIS 11 +#define DATA_SHIELDGENERATOR1 12 +#define DATA_SHIELDGENERATOR2 13 +#define DATA_SHIELDGENERATOR3 14 +#define DATA_SHIELDGENERATOR4 15 +#define DATA_THELURKERBELOWEVENT 16 +#define DATA_TIDALVESS 17 +#define DATA_FATHOMLORDKARATHRESSEVENT 18 +#endif diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp new file mode 100644 index 00000000000..d1a90449abf --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp @@ -0,0 +1,227 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Serpent_Shrine +SD%Complete: 0 +SDComment: VERIFY SCRIPT +SDCategory: Coilfang Resevoir, Serpent Shrine Cavern +EndScriptData */ + +#include "precompiled.h" +#include "def_serpent_shrine.h" + +#define ENCOUNTERS 6 + +/* Serpentshrine cavern encounters: +0 - Hydross The Unstable event +1 - Leotheras The Blind Event +2 - The Lurker Below Event +3 - Fathom-Lord Karathress Event +4 - Morogrim Tidewalker Event +5 - Lady Vashj Event +*/ + +struct MANGOS_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance +{ + instance_serpentshrine_cavern(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint64 Sharkkis; + uint64 Tidalvess; + uint64 Caribdis; + uint64 LadyVashj; + uint64 Karathress; + uint64 KarathressEvent_Starter; + + bool ShieldGeneratorDeactivated[4]; + + bool Encounters[ENCOUNTERS]; + + void Initialize() + { + Sharkkis = 0; + Tidalvess = 0; + Caribdis = 0; + LadyVashj = 0; + Karathress = 0; + KarathressEvent_Starter = 0; + + ShieldGeneratorDeactivated[0] = false; + ShieldGeneratorDeactivated[1] = false; + ShieldGeneratorDeactivated[2] = false; + ShieldGeneratorDeactivated[3] = false; + + for(uint8 i = 0; i < ENCOUNTERS; i++) + Encounters[i] = false; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < ENCOUNTERS; i++) + if(Encounters[i]) return true; + + return false; + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case 21212: LadyVashj = creature->GetGUID(); break; + case 21214: Karathress = creature->GetGUID(); break; + case 21966: Sharkkis = creature->GetGUID(); break; + case 21965: Tidalvess = creature->GetGUID(); break; + case 21964: Caribdis = creature->GetGUID(); break; + } + } + + void SetData64(uint32 type, uint64 data) + { + if(type == DATA_KARATHRESSEVENT_STARTER) + KarathressEvent_Starter = data; + } + + uint64 GetData64(uint32 identifier) + { + switch(identifier) + { + case DATA_SHARKKIS: + return Sharkkis; + case DATA_TIDALVESS: + return Tidalvess; + case DATA_CARIBDIS: + return Caribdis; + case DATA_LADYVASHJ: + return LadyVashj; + case DATA_KARATHRESS: + return Karathress; + case DATA_KARATHRESSEVENT_STARTER: + return KarathressEvent_Starter; + } + return 0; + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_HYDROSSTHEUNSTABLEEVENT: + Encounters[0] = (data) ? true : false; + break; + + case DATA_LEOTHERASTHEBLINDEVENT: + Encounters[1] = (data) ? true : false; + break; + + case DATA_THELURKERBELOWEVENT: + Encounters[2] = (data) ? true : false; + break; + + case DATA_KARATHRESSEVENT: + Encounters[3] = (data) ? true : false; + break; + + case DATA_MOROGRIMTIDEWALKEREVENT: + Encounters[4] = (data) ? true : false; + break; + //Lady Vashj + case DATA_LADYVASHJEVENT: + if(data == 0) + { + ShieldGeneratorDeactivated[0] = false; + ShieldGeneratorDeactivated[1] = false; + ShieldGeneratorDeactivated[2] = false; + ShieldGeneratorDeactivated[3] = false; + } + Encounters[5] = (data) ? true : false; + break; + + case DATA_SHIELDGENERATOR1: + ShieldGeneratorDeactivated[0] = (data) ? true : false; + break; + + case DATA_SHIELDGENERATOR2: + ShieldGeneratorDeactivated[1] = (data) ? true : false; + break; + + case DATA_SHIELDGENERATOR3: + ShieldGeneratorDeactivated[2] = (data) ? true : false; + break; + + case DATA_SHIELDGENERATOR4: + ShieldGeneratorDeactivated[3] = (data) ? true : false; + break; + } + } + + uint32 GetData(uint32 type) + { + switch(type) + { + case DATA_HYDROSSTHEUNSTABLEEVENT: + return Encounters[0]; + + case DATA_LEOTHERASTHEBLINDEVENT: + return Encounters[1]; + + case DATA_THELURKERBELOWEVENT: + return Encounters[2]; + + case DATA_KARATHRESSEVENT: + return Encounters[3]; + + case DATA_MOROGRIMTIDEWALKEREVENT: + return Encounters[4]; + + //Lady Vashj + case DATA_LADYVASHJEVENT: + return Encounters[5]; + + case DATA_SHIELDGENERATOR1: + return ShieldGeneratorDeactivated[0]; + + case DATA_SHIELDGENERATOR2: + return ShieldGeneratorDeactivated[1]; + + case DATA_SHIELDGENERATOR3: + return ShieldGeneratorDeactivated[2]; + + case DATA_SHIELDGENERATOR4: + return ShieldGeneratorDeactivated[3]; + + case DATA_CANSTARTPHASE3: + if(ShieldGeneratorDeactivated[0] && ShieldGeneratorDeactivated[1] && ShieldGeneratorDeactivated[2] && ShieldGeneratorDeactivated[3]) + return 1; + break; + } + + return 0; + } +}; + +InstanceData* GetInstanceData_instance_serpentshrine_cavern(Map* map) +{ + return new instance_serpentshrine_cavern(map); +} + +void AddSC_instance_serpentshrine_cavern() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_serpent_shrine"; + newscript->GetInstanceData = GetInstanceData_instance_serpentshrine_cavern; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/slave_pens/boss_rokmar.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/slave_pens/boss_rokmar.cpp new file mode 100644 index 00000000000..7d0e9d72822 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/slave_pens/boss_rokmar.cpp @@ -0,0 +1,65 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Rokmar +SD%Complete: 100 +SDComment: +SDCategory: Coilfang Resevoir, Slave Pens +EndScriptData */ + +#include "../../../creature/simple_ai.h" + +#define SPELL_WATTER_SPIT 40086 +#define SPELL_GRIEVOUS_WOUND 31956 +#define SPELL_ENSARING_MOSS 31948 + +CreatureAI* GetAI_boss_rokmar_the_crackler(Creature *_Creature) +{ + SimpleAI* ai = new SimpleAI (_Creature); + + //Watter Spit + ai->Spell[0].Enabled = true; + ai->Spell[0].Spell_Id = SPELL_WATTER_SPIT; + ai->Spell[0].Cooldown = 15000; + ai->Spell[0].First_Cast = 15000; + ai->Spell[0].Cast_Target_Type = CAST_HOSTILE_TARGET; + + //Grievous Wound + ai->Spell[1].Enabled = true; + ai->Spell[1].Spell_Id = SPELL_GRIEVOUS_WOUND; + ai->Spell[1].Cooldown = 25000; + ai->Spell[1].First_Cast = 15000; + ai->Spell[1].Cast_Target_Type = CAST_HOSTILE_TARGET; + + //Ensaring Moss + ai->Spell[2].Enabled = true; + ai->Spell[2].Spell_Id = SPELL_ENSARING_MOSS; + ai->Spell[2].Cooldown = 15000 + (rand()%10000); + ai->Spell[2].First_Cast = 25000; + ai->Spell[2].Cast_Target_Type = CAST_HOSTILE_TARGET; + + return ai; +} + +void AddSC_boss_rokmar_the_crackler() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_rokmar_the_crackler"; + newscript->GetAI = GetAI_boss_rokmar_the_crackler; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp new file mode 100644 index 00000000000..74fb09726c7 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp @@ -0,0 +1,223 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Hydromancer_Thespia +SD%Complete: 80 +SDComment: Normal/heroic mode: to be tested. Needs additional adjustments (when instance script is adjusted) +SDCategory: Coilfang Resevoir, The Steamvault +EndScriptData */ + +/* ContentData +boss_hydromancer_thespia +mob_coilfang_waterelemental +EndContentData */ + +#include "precompiled.h" +#include "def_steam_vault.h" + +#define SAY_SUMMON "Surge forth my pets!" +#define SOUND_SUMMON 10360 + +#define SAY_AGGRO_1 "The depths will consume you!" +#define SOUND_AGGRO_1 10361 +#define SAY_AGGRO_2 "Meet your doom, surface dwellers!" +#define SOUND_AGGRO_2 10362 +#define SAY_AGGRO_3 "You will drown in blood!" +#define SOUND_AGGRO_3 10363 + +#define SAY_SLAY_1 "To the depths of oblivion with you!" +#define SOUND_SLAY_1 10364 +#define SAY_SLAY_2 "For my lady and master!" +#define SOUND_SLAY_2 10365 + +#define SAY_DEAD "Our matron will be.. the end of.. you.." +#define SOUND_DEAD 10366 + +#define SPELL_LIGHTNING_CLOUD 25033 +#define SPELL_LUNG_BURST 31481 +#define SPELL_ENVELOPING_WINDS 31718 + +struct MANGOS_DLL_DECL boss_thespiaAI : public ScriptedAI +{ + boss_thespiaAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + bool HeroicMode; + + uint32 LightningCloud_Timer; + uint32 LungBurst_Timer; + uint32 EnvelopingWinds_Timer; + + void Reset() + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + + LightningCloud_Timer = 28000; + LungBurst_Timer = 7000; + EnvelopingWinds_Timer = 9000; + + if( pInstance ) pInstance->SetData(TYPE_HYDROMANCER_THESPIA, NOT_STARTED); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEAD, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEAD); + + if( pInstance ) pInstance->SetData(TYPE_HYDROMANCER_THESPIA, DONE); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_1); + break; + case 1: + DoYell(SAY_SLAY_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_2); + break; + } + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + break; + case 1: + DoYell(SAY_AGGRO_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + break; + case 2: + DoYell(SAY_AGGRO_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + break; + } + + if( pInstance ) pInstance->SetData(TYPE_HYDROMANCER_THESPIA, IN_PROGRESS); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //LightningCloud_Timer + if( LightningCloud_Timer < diff ) + { + //cast twice in Heroic mode + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target, SPELL_LIGHTNING_CLOUD); + if( HeroicMode ) + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target, SPELL_LIGHTNING_CLOUD); + LightningCloud_Timer = 28000; + }else LightningCloud_Timer -=diff; + + //LungBurst_Timer + if( LungBurst_Timer < diff ) + { + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target, SPELL_LUNG_BURST); + LungBurst_Timer = 10000+rand()%5000; + }else LungBurst_Timer -=diff; + + //EnvelopingWinds_Timer + if( EnvelopingWinds_Timer < diff ) + { + //cast twice in Heroic mode + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target, SPELL_ENVELOPING_WINDS); + if( HeroicMode ) + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target, SPELL_ENVELOPING_WINDS); + EnvelopingWinds_Timer = 10000+rand()%5000; + }else EnvelopingWinds_Timer -=diff; + + DoMeleeAttackIfReady(); + } +}; + +#define SPELL_WATER_BOLT_VOLLEY 34449 +#define H_SPELL_WATER_BOLT_VOLLEY 37924 + +struct MANGOS_DLL_DECL mob_coilfang_waterelementalAI : public ScriptedAI +{ + mob_coilfang_waterelementalAI(Creature *c) : ScriptedAI(c) {Reset();} + + bool HeroicMode; + uint32 WaterBoltVolley_Timer; + + void Reset() + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + WaterBoltVolley_Timer = 3000+rand()%3000; + } + + void Aggro(Unit *who) { } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( WaterBoltVolley_Timer < diff ) + { + if( HeroicMode ) DoCast(m_creature,H_SPELL_WATER_BOLT_VOLLEY); + else DoCast(m_creature,SPELL_WATER_BOLT_VOLLEY); + + WaterBoltVolley_Timer = 10000+rand()%5000; + }else WaterBoltVolley_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_thespiaAI(Creature *_Creature) +{ + return new boss_thespiaAI (_Creature); +} + +CreatureAI* GetAI_mob_coilfang_waterelementalAI(Creature *_Creature) +{ + return new mob_coilfang_waterelementalAI (_Creature); +} + +void AddSC_boss_hydromancer_thespia() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_hydromancer_thespia"; + newscript->GetAI = GetAI_boss_thespiaAI; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_coilfang_waterelemental"; + newscript->GetAI = GetAI_mob_coilfang_waterelementalAI; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp new file mode 100644 index 00000000000..83177d88fa2 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp @@ -0,0 +1,297 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Mekgineer_Steamrigger +SD%Complete: 60 +SDComment: Mechanics' interrrupt heal doesn't work very well, also a proper movement needs to be implemented -> summon further away and move towards target to repair. +SDCategory: Coilfang Resevoir, The Steamvault +EndScriptData */ + +/* ContentData +boss_mekgineer_steamrigger +mob_steamrigger_mechanic +EndContentData */ + +#include "precompiled.h" +#include "def_steam_vault.h" + +#define SAY_MECHANICS "I'm bringin' the pain!" +#define SOUND_MECHANICS 10367 + +#define SAY_AGGRO_1 "You're in for a world of hurt!" +#define SOUND_AGGRO_1 10368 +#define SAY_AGGRO_2 "Eat hot metal, scumbag!" +#define SOUND_AGGRO_2 10369 +#define SAY_AGGRO_3 "I'll come over there!" +#define SOUND_AGGRO_3 10370 +#define SAY_AGGRO_4 "I'm bringin' the pain!" +#define SOUND_AGGRO_4 10371 + +#define SAY_SLAY_1 "You just got served, punk!" +#define SOUND_SLAY_1 10372 +#define SAY_SLAY_2 "I own you!" +#define SOUND_SLAY_2 10373 +#define SAY_SLAY_3 "Have fun dyin', cupcake!" +#define SOUND_SLAY_3 10374 + +#define SAY_DEATH "Mommy!" +#define SOUND_DEATH 10375 + +#define SPELL_SUPER_SHRINK_RAY 31485 +#define SPELL_SAW_BLADE 31486 +#define SPELL_ELECTRIFIED_NET 35107 +#define H_SPELL_ENRAGE 1 //corrent enrage spell not known + +#define ENTRY_STREAMRIGGER_MECHANIC 17951 + +struct MANGOS_DLL_DECL boss_mekgineer_steamriggerAI : public ScriptedAI +{ + boss_mekgineer_steamriggerAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 Shrink_Timer; + uint32 Saw_Blade_Timer; + uint32 Electrified_Net_Timer; + bool Summon75; + bool Summon50; + bool Summon25; + + void Reset() + { + Shrink_Timer = 20000; + Saw_Blade_Timer = 15000; + Electrified_Net_Timer = 10000; + + Summon75 = false; + Summon50 = false; + Summon25 = false; + + if( pInstance ) pInstance->SetData(TYPE_MEKGINEER_STEAMRIGGER, NOT_STARTED); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + + if( pInstance ) pInstance->SetData(TYPE_MEKGINEER_STEAMRIGGER, DONE); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_SLAY_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_1); + break; + case 1: + DoYell(SAY_SLAY_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_2); + break; + case 2: + DoYell(SAY_SLAY_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_3); + break; + } + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + break; + case 1: + DoYell(SAY_AGGRO_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + break; + case 2: + DoYell(SAY_AGGRO_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + break; + } + + if( pInstance ) pInstance->SetData(TYPE_MEKGINEER_STEAMRIGGER, IN_PROGRESS); + } + + //no known summon spells exist + void SummonMechanichs() + { + DoYell(SAY_MECHANICS, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_MECHANICS); + + DoSpawnCreature(ENTRY_STREAMRIGGER_MECHANIC,5,5,0,0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 240000); + DoSpawnCreature(ENTRY_STREAMRIGGER_MECHANIC,-5,5,0,0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 240000); + DoSpawnCreature(ENTRY_STREAMRIGGER_MECHANIC,-5,-5,0,0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 240000); + + if( rand()%2 ) + DoSpawnCreature(ENTRY_STREAMRIGGER_MECHANIC,5,-7,0,0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 240000); + if( rand()%2 ) + DoSpawnCreature(ENTRY_STREAMRIGGER_MECHANIC,7,-5,0,0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 240000); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( Shrink_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_SUPER_SHRINK_RAY); + Shrink_Timer = 20000; + }else Shrink_Timer -= diff; + + if( Saw_Blade_Timer < diff ) + { + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1) ) DoCast(target,SPELL_SAW_BLADE); + else DoCast(m_creature->getVictim(),SPELL_SAW_BLADE); + Saw_Blade_Timer = 15000; + } else Saw_Blade_Timer -= diff; + + if( Electrified_Net_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_ELECTRIFIED_NET); + Electrified_Net_Timer = 10000; + } + else Electrified_Net_Timer -= diff; + + if( !Summon75 ) + if( (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 75 ) + { + SummonMechanichs(); + Summon75 = true; + } + if( !Summon50 ) + if( (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 50 ) + { + SummonMechanichs(); + Summon50 = true; + } + if( !Summon25 ) + if( (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 25 ) + { + SummonMechanichs(); + Summon25 = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_mekgineer_steamrigger(Creature *_Creature) +{ + return new boss_mekgineer_steamriggerAI (_Creature); +} + +#define SPELL_DISPEL_MAGIC 17201 +#define SPELL_REPAIR 31532 +#define H_SPELL_REPAIR 37936 + +#define MAX_REPAIR_RANGE (13.0f) //we should be at least at this range for repair +#define MIN_REPAIR_RANGE (7.0f) //we can stop movement at this range to repair but not required + +struct MANGOS_DLL_DECL mob_steamrigger_mechanicAI : public ScriptedAI +{ + mob_steamrigger_mechanicAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 Repair_Timer; + bool HeroicMode; + + void Reset() + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + Repair_Timer = 2000; + } + + void MoveInLineOfSight(Unit* who) + { + //react only if attacked + return; + } + + void Aggro(Unit *who) { } + + void UpdateAI(const uint32 diff) + { + if( Repair_Timer < diff ) + { + if( pInstance && pInstance->GetData64(DATA_MEKGINEERSTEAMRIGGER) && pInstance->GetData(TYPE_MEKGINEER_STEAMRIGGER) == IN_PROGRESS) + { + if( Unit* pMekgineer = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MEKGINEERSTEAMRIGGER)) ) + { + if( m_creature->IsWithinDistInMap(pMekgineer, MAX_REPAIR_RANGE) ) + { + //are we already channeling? Doesn't work very well, find better check? + if( !m_creature->GetUInt32Value(UNIT_CHANNEL_SPELL) ) + { + //m_creature->GetMotionMaster()->MovementExpired(); + //m_creature->GetMotionMaster()->MoveIdle(); + + if( HeroicMode ) DoCast(m_creature,H_SPELL_REPAIR,true); + else DoCast(m_creature,SPELL_REPAIR,true); + } + Repair_Timer = 5000; + } + else + { + //m_creature->GetMotionMaster()->MovementExpired(); + //m_creature->GetMotionMaster()->MoveFollow(pMekgineer,0,0); + } + } + }else Repair_Timer = 5000; + }else Repair_Timer -= diff; + + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_steamrigger_mechanic(Creature *_Creature) +{ + return new mob_steamrigger_mechanicAI (_Creature); +} + +void AddSC_boss_mekgineer_steamrigger() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_mekgineer_steamrigger"; + newscript->GetAI = GetAI_boss_mekgineer_steamrigger; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_steamrigger_mechanic"; + newscript->GetAI = GetAI_mob_steamrigger_mechanic; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp new file mode 100644 index 00000000000..49172341514 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp @@ -0,0 +1,255 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Warlord_Kalithres +SD%Complete: 65 +SDComment: Contains workarounds regarding warlord's rage spells not acting as expected. Both scripts here require review and fine tuning. +SDCategory: Coilfang Resevoir, The Steamvault +EndScriptData */ + +#include "precompiled.h" +#include "def_steam_vault.h" + +#define SAY_INTRO "You deem yourselves worthy simply because you bested my guards? Our work here will not be compromised!" +#define SOUND_INTRO 10390 + +#define SAY_REGEN "This is not nearly over..." +#define SOUND_REGEN 10391 + +#define SAY_AGGRO1 "Your head will roll!" +#define SOUND_AGGRO1 10392 +#define SAY_AGGRO2 "I despise all of your kind!" +#define SOUND_AGGRO2 10393 +#define SAY_AGGRO3 "Ba'ahntha sol'dorei!" +#define SOUND_AGGRO3 10394 + +#define SAY_SLAY1 "Scram, surface filth!" +#define SOUND_SLAY1 10395 +#define SAY_SLAY2 "Ah ha ha ha ha ha ha!" +#define SOUND_SLAY2 10396 + +#define SAY_DEATH "For her Excellency... for... Vashj!" +#define SOUND_DEATH 10397 + +#define SPELL_SPELL_REFLECTION 31534 +#define SPELL_IMPALE 39061 +#define SPELL_WARLORDS_RAGE 37081 +#define SPELL_WARLORDS_RAGE_NAGA 31543 + +#define SPELL_WARLORDS_RAGE_PROC 36453 + +struct MANGOS_DLL_DECL mob_naga_distillerAI : public ScriptedAI +{ + mob_naga_distillerAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + void Reset() + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + //hack, due to really weird spell behaviour :( + if( pInstance ) + if( pInstance->GetData(TYPE_DISTILLER) == IN_PROGRESS ) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + } + + void Aggro(Unit *who) { } + + void StartRageGen(Unit *caster) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + DoCast(m_creature,SPELL_WARLORDS_RAGE_NAGA,true); + if( pInstance ) pInstance->SetData(TYPE_DISTILLER,IN_PROGRESS); + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if( m_creature->GetHealth() <= damage ) + if( pInstance ) pInstance->SetData(TYPE_DISTILLER,DONE); + } +}; + +struct MANGOS_DLL_DECL boss_warlord_kalithreshAI : public ScriptedAI +{ + boss_warlord_kalithreshAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 Reflection_Timer; + uint32 Impale_Timer; + uint32 Rage_Timer; + bool CanRage; + + void Reset() + { + Reflection_Timer = 10000; + Impale_Timer = 30000; + Rage_Timer = 45000; + CanRage = false; + + if( pInstance ) pInstance->SetData(TYPE_WARLORD_KALITHRESH, NOT_STARTED); + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO3); + break; + } + + if( pInstance ) pInstance->SetData(TYPE_WARLORD_KALITHRESH, IN_PROGRESS); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(victim, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(victim, SOUND_SLAY2); + break; + } + } + + Creature* SelectCreatureInGrid(uint32 entry, float range) + { + Creature* pCreature = NULL; + + CellPair pair(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY())); + Cell cell(pair); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck creature_check(*m_creature, entry, true, range); + MaNGOS::CreatureLastSearcher searcher(pCreature, creature_check); + + TypeContainerVisitor, GridTypeMapContainer> creature_searcher(searcher); + + CellLock cell_lock(cell, pair); + cell_lock->Visit(cell_lock, creature_searcher,*(m_creature->GetMap())); + + return pCreature; + } + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + //hack :( + if( spell->Id == SPELL_WARLORDS_RAGE_PROC ) + if( pInstance ) + if( pInstance->GetData(TYPE_DISTILLER) == DONE ) + m_creature->RemoveAurasDueToSpell(SPELL_WARLORDS_RAGE_PROC); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if( pInstance ) pInstance->SetData(TYPE_WARLORD_KALITHRESH, DONE); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( Rage_Timer < diff ) + { + Creature* distiller = SelectCreatureInGrid(17954, 100); + if( distiller ) + { + DoYell(SAY_REGEN, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_REGEN); + DoCast(m_creature,SPELL_WARLORDS_RAGE); + ((mob_naga_distillerAI*)distiller->AI())->StartRageGen(m_creature); + } + Rage_Timer = 45000; + }else Rage_Timer -= diff; + + //Reflection_Timer + if( Reflection_Timer < diff ) + { + DoCast(m_creature, SPELL_SPELL_REFLECTION); + Reflection_Timer = 20000+rand()%5000; + }else Reflection_Timer -= diff; + + //Impale_Timer + if( Impale_Timer < diff ) + { + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_IMPALE); + + Impale_Timer = 7500+rand()%5000; + }else Impale_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_naga_distiller(Creature *_Creature) +{ + return new mob_naga_distillerAI (_Creature); +} + +CreatureAI* GetAI_boss_warlord_kalithresh(Creature *_Creature) +{ + return new boss_warlord_kalithreshAI (_Creature); +} + +void AddSC_boss_warlord_kalithresh() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mob_naga_distiller"; + newscript->GetAI = GetAI_mob_naga_distiller; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_warlord_kalithresh"; + newscript->GetAI = GetAI_boss_warlord_kalithresh; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/def_steam_vault.h b/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/def_steam_vault.h new file mode 100644 index 00000000000..0f8ae80fe2c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/def_steam_vault.h @@ -0,0 +1,16 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_STEAM_VAULT_H +#define DEF_STEAM_VAULT_H + +#define TYPE_HYDROMANCER_THESPIA 1 +#define TYPE_MEKGINEER_STEAMRIGGER 2 +#define TYPE_WARLORD_KALITHRESH 3 +#define TYPE_DISTILLER 4 + +#define DATA_MEKGINEERSTEAMRIGGER 5 +#define DATA_KALITRESH 6 +#define DATA_THESPIA 7 +#endif diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/instance_steam_vault.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/instance_steam_vault.cpp new file mode 100644 index 00000000000..0e524f3d4b8 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/steam_vault/instance_steam_vault.cpp @@ -0,0 +1,170 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Steam_Vault +SD%Complete: 60 +SDComment: Workaround for opening of Main Chamber door. Mangos does not support scripting of Gameobject as this instance require. +SDCategory: Coilfang Resevoir, The Steamvault +EndScriptData */ + +#include "precompiled.h" +#include "def_steam_vault.h" + +#define ENCOUNTERS 4 + +#define MAIN_CHAMBERS_DOOR 183049 //door opened when hydromancer and mekgineer are died +#define ACCESS_PANEL_HYDRO 184125 +#define ACCESS_PANEL_MEK 184126 + +/* Steam Vaults encounters: +1 - Hydromancer Thespia Event +2 - Mekgineer Steamrigger Event +3 - Warlord Kalithresh Event +*/ + +struct MANGOS_DLL_DECL instance_steam_vault : public ScriptedInstance +{ + instance_steam_vault(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint64 ThespiaGUID; + uint64 MekgineerGUID; + uint64 KalithreshGUID; + uint32 Encounter[ENCOUNTERS]; + + bool IsHydromancerDead, IsMekgineerDead; + GameObject *MainChambersDoor; + GameObject *AccessPanelHydro; + GameObject *AccessPanelMek; + + void Initialize() + { + ThespiaGUID = 0; + MekgineerGUID = 0; + KalithreshGUID = 0; + IsHydromancerDead = false; + IsMekgineerDead = false; + MainChambersDoor = NULL; + AccessPanelHydro = NULL; + AccessPanelMek = NULL; + + for(uint8 i = 0; i < ENCOUNTERS; i++) + Encounter[i] = false; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < ENCOUNTERS; i++) + if( Encounter[i] ) return true; + + return false; + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case 17797: ThespiaGUID = creature->GetGUID(); break; + case 17796: MekgineerGUID = creature->GetGUID(); break; + case 17798: KalithreshGUID = creature->GetGUID(); break; + } + } + + void OnObjectCreate(GameObject *go) + { + switch(go->GetEntry()) + { + case MAIN_CHAMBERS_DOOR: MainChambersDoor = go; break; + case ACCESS_PANEL_HYDRO: AccessPanelHydro = go; break; + case ACCESS_PANEL_MEK: AccessPanelMek = go; break; + } + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case TYPE_HYDROMANCER_THESPIA: + if(data == DONE) + { + IsHydromancerDead = true; + if( IsMekgineerDead && MainChambersDoor ) + MainChambersDoor->SetGoState(0); + } + Encounter[0] = data; + break; + case TYPE_MEKGINEER_STEAMRIGGER: + if(data == DONE) + { + IsMekgineerDead = true; + if( IsHydromancerDead && MainChambersDoor ) + MainChambersDoor->SetGoState(0); + } + Encounter[1] = data; + break; + case TYPE_WARLORD_KALITHRESH: + Encounter[2] = data; + break; + case TYPE_DISTILLER: + Encounter[3] = data; + break; + } + } + + uint32 GetData(uint32 type) + { + switch(type) + { + case TYPE_HYDROMANCER_THESPIA: + return Encounter[0]; + case TYPE_MEKGINEER_STEAMRIGGER: + return Encounter[1]; + case TYPE_WARLORD_KALITHRESH: + return Encounter[2]; + case TYPE_DISTILLER: + return Encounter[3]; + } + return 0; + } + + uint64 GetData64(uint32 data) + { + switch(data) + { + case DATA_THESPIA: + return ThespiaGUID; + case DATA_MEKGINEERSTEAMRIGGER: + return MekgineerGUID; + case DATA_KALITRESH: + return KalithreshGUID; + } + return 0; + } +}; + +InstanceData* GetInstanceData_instance_steam_vault(Map* map) +{ + return new instance_steam_vault(map); +} + +void AddSC_instance_steam_vault() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_steam_vault"; + newscript->GetInstanceData = GetInstanceData_instance_steam_vault; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/underbog/boss_ghazan.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/underbog/boss_ghazan.cpp new file mode 100644 index 00000000000..6bb2635fa7b --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/underbog/boss_ghazan.cpp @@ -0,0 +1,78 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Ghazan +SD%Complete: 100 +SDComment: +SDCategory: Coilfang Resevoir, Underbog +EndScriptData */ + +#include "../../../creature/simple_ai.h" + +/* +--== Ghaz'an ==-- +*Acid Spit - 34290; timer: 8sec +*Enrage - 20% hp; 40683 +*Tail Sweep - 34267; timer: ~10sec +*Acid Breath - 34268; timer: 5sec +*/ + +CreatureAI* GetAI_boss_ghazan(Creature *_Creature) +{ + SimpleAI* ai = new SimpleAI (_Creature); + + // Acid Spit - 34290; timer: 8sec + ai->Spell[0].Enabled = true; + ai->Spell[0].Spell_Id = 34290; + ai->Spell[0].Cooldown = 8000; + ai->Spell[0].First_Cast = 8000; + ai->Spell[0].Cast_Target_Type = CAST_SELF; + + // Acid Breath - 34268; timer: 5sec + ai->Spell[1].Enabled = true; + ai->Spell[1].Spell_Id = 34268; + ai->Spell[1].Cooldown = 5000; + ai->Spell[1].First_Cast = 5000; + ai->Spell[1].Cast_Target_Type = CAST_SELF; + + // Tail Sweep - 34267 + ai->Spell[2].Enabled = true; + ai->Spell[2].Spell_Id = 34267; + ai->Spell[2].Cooldown = 10000; + ai->Spell[2].First_Cast = 10000; + ai->Spell[2].Cast_Target_Type = CAST_SELF; + + // Enrage - 20% hp; 40683 + ai->Spell[3].Enabled = true; + ai->Spell[3].Spell_Id = 40683; + ai->Spell[3].Cooldown = -1; + ai->Spell[3].First_Cast = -80; + ai->Spell[3].Cast_Target_Type = CAST_SELF; + + ai->EnterEvadeMode(); + + return ai; +} + +void AddSC_boss_ghazan() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_ghazan"; + newscript->GetAI = GetAI_boss_ghazan; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/coilfang_resevoir/underbog/boss_hungarfen.cpp b/src/bindings/scripts/scripts/zone/coilfang_resevoir/underbog/boss_hungarfen.cpp new file mode 100644 index 00000000000..663db35a946 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/coilfang_resevoir/underbog/boss_hungarfen.cpp @@ -0,0 +1,156 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Hungarfen +SD%Complete: 95 +SDComment: Need confirmation if spell data are same in both modes. Summons should have faster rate in heroic +SDCategory: Coilfang Resevoir, Underbog +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FOUL_SPORES 31673 +#define SPELL_ACID_GEYSER 38739 + +struct MANGOS_DLL_DECL boss_hungarfenAI : public ScriptedAI +{ + boss_hungarfenAI(Creature *c) : ScriptedAI(c) + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + Reset(); + } + + bool HeroicMode; + bool Root; + uint32 Mushroom_Timer; + uint32 AcidGeyser_Timer; + + void Reset() + { + Root = false; + Mushroom_Timer = 5000; // 1 mushroom after 5s, then one per 10s. This should be different in heroic mode + AcidGeyser_Timer = 10000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( (m_creature->GetHealth()*100) / m_creature->GetMaxHealth() <= 20 ) + { + if( !Root ) + { + DoCast(m_creature,SPELL_FOUL_SPORES); + Root = true; + } + } + + if( Mushroom_Timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + m_creature->SummonCreature(17990, target->GetPositionX()+(rand()%8), target->GetPositionY()+(rand()%8), target->GetPositionZ(), (rand()%5), TEMPSUMMON_TIMED_DESPAWN, 22000); + else + m_creature->SummonCreature(17990, m_creature->GetPositionX()+(rand()%8), m_creature->GetPositionY()+(rand()%8), m_creature->GetPositionZ(), (rand()%5), TEMPSUMMON_TIMED_DESPAWN, 22000); + + Mushroom_Timer = 10000; + }else Mushroom_Timer -= diff; + + if( AcidGeyser_Timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_ACID_GEYSER); + AcidGeyser_Timer = 10000+rand()%7500; + }else AcidGeyser_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_hungarfen(Creature *_Creature) +{ + return new boss_hungarfenAI (_Creature); +} + +#define SPELL_SPORE_CLOUD 34168 +#define SPELL_PUTRID_MUSHROOM 31690 +#define SPELL_GROW 31698 + +struct MANGOS_DLL_DECL mob_underbog_mushroomAI : public ScriptedAI +{ + mob_underbog_mushroomAI(Creature *c) : ScriptedAI(c) { Reset(); } + + bool Stop; + uint32 Grow_Timer; + uint32 Shrink_Timer; + + void Reset() + { + Stop = false; + Grow_Timer = 0; + Shrink_Timer = 20000; + + DoCast(m_creature,SPELL_PUTRID_MUSHROOM,true); + DoCast(m_creature,SPELL_SPORE_CLOUD,true); + } + + void MoveInLineOfSight(Unit *who) { return; } + + void AttackStart(Unit* who) { return; } + + void Aggro(Unit* who) { } + + void UpdateAI(const uint32 diff) + { + if( Stop ) + return; + + if( Grow_Timer <= diff ) + { + DoCast(m_creature,SPELL_GROW); + Grow_Timer = 3000; + }else Grow_Timer -= diff; + + if( Shrink_Timer <= diff ) + { + m_creature->RemoveAurasDueToSpell(SPELL_GROW); + Stop = true; + }else Shrink_Timer -= diff; + } +}; +CreatureAI* GetAI_mob_underbog_mushroom(Creature *_Creature) +{ + return new mob_underbog_mushroomAI (_Creature); +} + +void AddSC_boss_hungarfen() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_hungarfen"; + newscript->GetAI = GetAI_boss_hungarfen; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_underbog_mushroom"; + newscript->GetAI = GetAI_mob_underbog_mushroom; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/darkshore/darkshore.cpp b/src/bindings/scripts/scripts/zone/darkshore/darkshore.cpp new file mode 100644 index 00000000000..02acd88a491 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/darkshore/darkshore.cpp @@ -0,0 +1,24 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Darkshore +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Darkshore +EndScriptData */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/scripts/zone/deadmines/deadmines.cpp b/src/bindings/scripts/scripts/zone/deadmines/deadmines.cpp new file mode 100644 index 00000000000..535d8c86410 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/deadmines/deadmines.cpp @@ -0,0 +1,24 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Deadmines +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Deadmines +EndScriptData */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/scripts/zone/deadmines/instance_deadmines.cpp b/src/bindings/scripts/scripts/zone/deadmines/instance_deadmines.cpp new file mode 100644 index 00000000000..d97db663273 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/deadmines/instance_deadmines.cpp @@ -0,0 +1,22 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Deadmines +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Deadmines +EndScriptData */ diff --git a/src/bindings/scripts/scripts/zone/dun_morogh/dun_morogh.cpp b/src/bindings/scripts/scripts/zone/dun_morogh/dun_morogh.cpp new file mode 100644 index 00000000000..d584dc0690e --- /dev/null +++ b/src/bindings/scripts/scripts/zone/dun_morogh/dun_morogh.cpp @@ -0,0 +1,98 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Dun_Morogh +SD%Complete: 50 +SDComment: Quest support: 1783 +SDCategory: Dun Morogh +EndScriptData */ + +/* ContentData +npc_narm_faulk +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_narm_faulk +######*/ + +#define SAY_HEAL "Thank you, dear Paladin, you just saved my life." + +struct MANGOS_DLL_DECL npc_narm_faulkAI : public ScriptedAI +{ + uint32 lifeTimer; + bool spellHit; + + npc_narm_faulkAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + lifeTimer = 120000; + m_creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 32); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,7); // lay down + spellHit = false; + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + return; + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->GetUInt32Value(UNIT_FIELD_BYTES_1)) + { + if(lifeTimer < diff) + m_creature->AI()->EnterEvadeMode(); + else + lifeTimer -= diff; + } + } + + void SpellHit(Unit *Hitter, const SpellEntry *Spellkind) + { + if(Spellkind->Id == 8593 && !spellHit) + { + DoCast(m_creature,32343); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,0); + m_creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0); + //m_creature->RemoveAllAuras(); + DoSay(SAY_HEAL,LANG_COMMON,NULL); + spellHit = true; + } + } + +}; +CreatureAI* GetAI_npc_narm_faulk(Creature *_Creature) +{ + return new npc_narm_faulkAI (_Creature); +} + +void AddSC_dun_morogh() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_narm_faulk"; + newscript->GetAI = GetAI_npc_narm_faulk; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/dustwallow_marsh/dustwallow_marsh.cpp b/src/bindings/scripts/scripts/zone/dustwallow_marsh/dustwallow_marsh.cpp new file mode 100644 index 00000000000..a29dda1c811 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/dustwallow_marsh/dustwallow_marsh.cpp @@ -0,0 +1,231 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Dustwallow_Marsh +SD%Complete: 95 +SDComment: Quest support: 11180, 558, 11126. Vendor Nat Pagle +SDCategory: Dustwallow Marsh +EndScriptData */ + +/* ContentData +mobs_risen_husk_spirit +npc_restless_apparition +npc_deserter_agitator +npc_lady_jaina_proudmoore +npc_nat_pagle +EndContentData */ + +#include "precompiled.h" + +/*###### +## mobs_risen_husk_spirit +######*/ + +#define SPELL_SUMMON_RESTLESS_APPARITION 42511 +#define SPELL_CONSUME_FLESH 37933 //Risen Husk +#define SPELL_INTANGIBLE_PRESENCE 43127 //Risen Spirit + +struct MANGOS_DLL_DECL mobs_risen_husk_spiritAI : public ScriptedAI +{ + mobs_risen_husk_spiritAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ConsumeFlesh_Timer; + uint32 IntangiblePresence_Timer; + + void Reset() + { + ConsumeFlesh_Timer = 10000; + IntangiblePresence_Timer = 5000; + } + + void Aggro(Unit* who) { } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if( done_by->GetTypeId() == TYPEID_PLAYER ) + if( damage >= m_creature->GetHealth() && ((Player*)done_by)->GetQuestStatus(11180) == QUEST_STATUS_INCOMPLETE ) + m_creature->CastSpell(done_by,SPELL_SUMMON_RESTLESS_APPARITION,false); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( ConsumeFlesh_Timer < diff ) + { + if( m_creature->GetEntry() == 23555 ) + DoCast(m_creature->getVictim(),SPELL_CONSUME_FLESH); + ConsumeFlesh_Timer = 15000; + } else ConsumeFlesh_Timer -= diff; + + if( IntangiblePresence_Timer < diff ) + { + if( m_creature->GetEntry() == 23554 ) + DoCast(m_creature->getVictim(),SPELL_INTANGIBLE_PRESENCE); + IntangiblePresence_Timer = 20000; + } else IntangiblePresence_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_mobs_risen_husk_spirit(Creature *_Creature) +{ + return new mobs_risen_husk_spiritAI (_Creature); +} + +/*###### +## npc_restless_apparition +######*/ + +bool GossipHello_npc_restless_apparition(Player *player, Creature *_Creature) +{ + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + player->TalkedToCreature(_Creature->GetEntry(), _Creature->GetGUID()); + _Creature->SetInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + return true; +} + +/*###### +## npc_deserter_agitator +######*/ + +struct MANGOS_DLL_DECL npc_deserter_agitatorAI : public ScriptedAI +{ + npc_deserter_agitatorAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + m_creature->setFaction(894); + } + + void Aggro(Unit* who) {} +}; + +CreatureAI* GetAI_npc_deserter_agitator(Creature *_Creature) +{ + return new npc_deserter_agitatorAI (_Creature); +} + +bool GossipHello_npc_deserter_agitator(Player *player, Creature *_Creature) +{ + if (player->GetQuestStatus(11126) == QUEST_STATUS_INCOMPLETE) + { + _Creature->setFaction(1883); + player->TalkedToCreature(_Creature->GetEntry(), _Creature->GetGUID()); + } + else + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +/*###### +## npc_lady_jaina_proudmoore +######*/ + +#define GOSSIP_ITEM_JAINA "I know this is rather silly but i have a young ward who is a bit shy and would like your autograph." + +bool GossipHello_npc_lady_jaina_proudmoore(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( player->GetQuestStatus(558) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_JAINA, GOSSIP_SENDER_MAIN, GOSSIP_SENDER_INFO ); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_lady_jaina_proudmoore(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_SENDER_INFO ) + { + player->SEND_GOSSIP_MENU( 7012, _Creature->GetGUID() ); + player->CastSpell( player, 23122, false); + } + return true; +} + +/*###### +## npc_nat_pagle +######*/ + +bool GossipHello_npc_nat_pagle(Player *player, Creature *_Creature) +{ + if(_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if(_Creature->isVendor() && player->GetQuestRewardStatus(8227)) + { + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + player->SEND_GOSSIP_MENU( 7640, _Creature->GetGUID() ); + } + else + player->SEND_GOSSIP_MENU( 7638, _Creature->GetGUID() ); + + return true; +} + +bool GossipSelect_npc_nat_pagle(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if(action == GOSSIP_ACTION_TRADE) + player->SEND_VENDORLIST( _Creature->GetGUID() ); + + return true; +} + +/*###### +## +######*/ + +void AddSC_dustwallow_marsh() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mobs_risen_husk_spirit"; + newscript->GetAI = GetAI_mobs_risen_husk_spirit; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_restless_apparition"; + newscript->pGossipHello = &GossipHello_npc_restless_apparition; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_deserter_agitator"; + newscript->GetAI = GetAI_npc_deserter_agitator; + newscript->pGossipHello = &GossipHello_npc_deserter_agitator; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_lady_jaina_proudmoore"; + newscript->pGossipHello = &GossipHello_npc_lady_jaina_proudmoore; + newscript->pGossipSelect = &GossipSelect_npc_lady_jaina_proudmoore; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_nat_pagle"; + newscript->pGossipHello = &GossipHello_npc_nat_pagle; + newscript->pGossipSelect = &GossipSelect_npc_nat_pagle; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/eastern_plaguelands/eastern_plaguelands.cpp b/src/bindings/scripts/scripts/zone/eastern_plaguelands/eastern_plaguelands.cpp new file mode 100644 index 00000000000..aa7ae7b2879 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/eastern_plaguelands/eastern_plaguelands.cpp @@ -0,0 +1,180 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Eastern_Plaguelands +SD%Complete: 100 +SDComment: Quest support: 5211, 5742. Special vendor Augustus the Touched +SDCategory: Eastern Plaguelands +EndScriptData */ + +/* ContentData +mobs_ghoul_flayer +npc_augustus_the_touched +npc_darrowshire_spirit +npc_tirion_fordring +EndContentData */ + +#include "precompiled.h" + +//id8530 - cannibal ghoul +//id8531 - gibbering ghoul +//id8532 - diseased flayer + +struct MANGOS_DLL_DECL mobs_ghoul_flayerAI : public ScriptedAI +{ + mobs_ghoul_flayerAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() { } + + void Aggro(Unit* who) { } + + void JustDied(Unit* Killer) + { + if( Killer->GetTypeId() == TYPEID_PLAYER ) + DoSpawnCreature(11064,0,0,0,0,TEMPSUMMON_TIMED_DESPAWN,60000); + } + +}; + +CreatureAI* GetAI_mobs_ghoul_flayer(Creature *_Creature) +{ + return new mobs_ghoul_flayerAI (_Creature); +} + +/*###### +## npc_augustus_the_touched +######*/ + +bool GossipHello_npc_augustus_the_touched(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( _Creature->isVendor() && player->GetQuestRewardStatus(6164) ) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(),_Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_augustus_the_touched(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_ACTION_TRADE ) + player->SEND_VENDORLIST( _Creature->GetGUID() ); + return true; +} + +/*###### +## npc_darrowshire_spirit +######*/ + +#define SPELL_SPIRIT_SPAWNIN 17321 + +struct MANGOS_DLL_DECL npc_darrowshire_spiritAI : public ScriptedAI +{ + npc_darrowshire_spiritAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + DoCast(m_creature,SPELL_SPIRIT_SPAWNIN); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit *who) { } + +}; +CreatureAI* GetAI_npc_darrowshire_spirit(Creature *_Creature) +{ + return new npc_darrowshire_spiritAI (_Creature); +} + +bool GossipHello_npc_darrowshire_spirit(Player *player, Creature *_Creature) +{ + player->SEND_GOSSIP_MENU(3873,_Creature->GetGUID()); + player->TalkedToCreature(_Creature->GetEntry(), _Creature->GetGUID()); + _Creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + return true; +} + +/*###### +## npc_tirion_fordring +######*/ + +bool GossipHello_npc_tirion_fordring(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(5742) == QUEST_STATUS_INCOMPLETE && player->getStandState() == PLAYER_STATE_SIT ) + player->ADD_GOSSIP_ITEM( 0, "I am ready to hear your tale, Tirion.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_tirion_fordring(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "Thank you, Tirion. What of your identity?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(4493, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "That is terrible.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(4494, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, "I will, Tirion.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->SEND_GOSSIP_MENU(4495, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(5742); + break; + } + return true; +} + +void AddSC_eastern_plaguelands() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mobs_ghoul_flayer"; + newscript->GetAI = GetAI_mobs_ghoul_flayer; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_augustus_the_touched"; + newscript->pGossipHello = &GossipHello_npc_augustus_the_touched; + newscript->pGossipSelect = &GossipSelect_npc_augustus_the_touched; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_darrowshire_spirit"; + newscript->GetAI = GetAI_npc_darrowshire_spirit; + newscript->pGossipHello = &GossipHello_npc_darrowshire_spirit; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_tirion_fordring"; + newscript->pGossipHello = &GossipHello_npc_tirion_fordring; + newscript->pGossipSelect = &GossipSelect_npc_tirion_fordring; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/elwynn_forest/elwynn_forest.cpp b/src/bindings/scripts/scripts/zone/elwynn_forest/elwynn_forest.cpp new file mode 100644 index 00000000000..0fc754f91c6 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/elwynn_forest/elwynn_forest.cpp @@ -0,0 +1,98 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Elwynn_Forest +SD%Complete: 50 +SDComment: Quest support: 1786 +SDCategory: Elwynn Forest +EndScriptData */ + +/* ContentData +npc_henze_faulk +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_henze_faulk +######*/ + +#define SAY_HEAL "Thank you, dear Paladin, you just saved my life." + +struct MANGOS_DLL_DECL npc_henze_faulkAI : public ScriptedAI +{ + uint32 lifeTimer; + bool spellHit; + + npc_henze_faulkAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + lifeTimer = 120000; + m_creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 32); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,7); // lay down + spellHit = false; + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + return; + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->GetUInt32Value(UNIT_FIELD_BYTES_1)) + { + if(lifeTimer < diff) + m_creature->AI()->EnterEvadeMode(); + else + lifeTimer -= diff; + } + } + + void SpellHit(Unit *Hitter, const SpellEntry *Spellkind) + { + if(Spellkind->Id == 8593 && !spellHit) + { + DoCast(m_creature,32343); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,0); + m_creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0); + //m_creature->RemoveAllAuras(); + DoSay(SAY_HEAL,LANG_COMMON,NULL); + spellHit = true; + } + } + +}; +CreatureAI* GetAI_npc_henze_faulk(Creature *_Creature) +{ + return new npc_henze_faulkAI (_Creature); +} + +void AddSC_elwynn_forest() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_henze_faulk"; + newscript->GetAI = GetAI_npc_henze_faulk; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/eversong_woods/eversong_woods.cpp b/src/bindings/scripts/scripts/zone/eversong_woods/eversong_woods.cpp new file mode 100644 index 00000000000..aab21ed5cb3 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/eversong_woods/eversong_woods.cpp @@ -0,0 +1,163 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Eversong_Woods +SD%Complete: 100 +SDComment: Quest support: 8346, 8483 +SDCategory: Eversong Woods +EndScriptData */ + +/* ContentData +mobs_mana_tapped +npc_prospector_anvilward +EndContentData */ + +#include "precompiled.h" +#include "../../npc/npc_escortAI.h" + +/*###### +## mobs_mana_tapped +######*/ + +struct MANGOS_DLL_DECL mobs_mana_tappedAI : public ScriptedAI +{ + mobs_mana_tappedAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() { } + + void Aggro(Unit *who) { } + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + if( caster->GetTypeId() == TYPEID_PLAYER) + if( ((Player*)caster)->GetQuestStatus(8346) == QUEST_STATUS_INCOMPLETE && !((Player*)caster)->GetReqKillOrCastCurrentCount(8346, m_creature->GetEntry()) && spell->Id == 28734) + ((Player*)caster)->CastedCreatureOrGO(15468, m_creature->GetGUID(), spell->Id); + return; + } +}; +CreatureAI* GetAI_mobs_mana_tapped(Creature *_Creature) +{ + return new mobs_mana_tappedAI (_Creature); +} + +/*###### +## npc_prospector_anvilward +######*/ + +#define QUEST_THE_DWARVEN_SPY 8483 + +struct MANGOS_DLL_DECL npc_prospector_anvilwardAI : public npc_escortAI +{ + // CreatureAI functions + npc_prospector_anvilwardAI(Creature *c) : npc_escortAI(c) {Reset();} + + // Pure Virtual Functions + void WaypointReached(uint32 i) + { + switch (i) + { + case 0: + m_creature->Say("Very well. Let's see what you have to show me, $N.", LANG_UNIVERSAL, PlayerGUID); + break; + case 5: + m_creature->Say("What manner of trick is this, $R? If you seek to ambush me, I warn you I will not go down quietly!", LANG_UNIVERSAL, PlayerGUID); + break; + case 6: + m_creature->setFaction(24); + break; + } + } + + void Aggro(Unit* who) { } + + void Reset() + { + //Default npc faction + m_creature->setFaction(35); + } + + void JustDied(Unit* killer) + { + //Default npc faction + m_creature->setFaction(35); + } + + void UpdateAI(const uint32 diff) + { + npc_escortAI::UpdateAI(diff); //Must update npc_escortAI + + } +}; + +CreatureAI* GetAI_npc_prospector_anvilward(Creature *_Creature) +{ + npc_prospector_anvilwardAI* thisAI = new npc_prospector_anvilwardAI(_Creature); + + thisAI->AddWaypoint(0, 9294.78, -6682.51, 22.42); + thisAI->AddWaypoint(1, 9298.27, -6667.99, 22.42); + thisAI->AddWaypoint(2, 9309.63, -6658.84, 22.43); + thisAI->AddWaypoint(3, 9304.43, -6649.31, 26.46); + thisAI->AddWaypoint(4, 9298.83, -6648.00, 28.61); + thisAI->AddWaypoint(5, 9291.06, -6653.46, 31.83,2500); + thisAI->AddWaypoint(6, 9289.08, -6660.17, 31.85,5000); + thisAI->AddWaypoint(7, 9291.06, -6653.46, 31.83); + + return (CreatureAI*)thisAI; +} + +bool GossipHello_npc_prospector_anvilward(Player *player, Creature *_Creature) +{ + if( player->GetQuestStatus(QUEST_THE_DWARVEN_SPY) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM(0, "I need a moment of your time, sir.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(8239, _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_prospector_anvilward(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch(action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "Why... yes, of course. I've something to show you right inside this building, Mr. Anvilward.",GOSSIP_SENDER_MAIN,GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(8240, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->CLOSE_GOSSIP_MENU(); + //attack,defend,walk + ((npc_escortAI*)(_Creature->AI()))->Start(true, true, false, player->GetGUID()); + break; + } + return true; +} + +void AddSC_eversong_woods() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mobs_mana_tapped"; + newscript->GetAI = GetAI_mobs_mana_tapped; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name= "npc_prospector_anvilward"; + newscript->GetAI = GetAI_npc_prospector_anvilward; + newscript->pGossipHello = &GossipHello_npc_prospector_anvilward; + newscript->pGossipSelect = &GossipSelect_npc_prospector_anvilward; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/felwood/felwood.cpp b/src/bindings/scripts/scripts/zone/felwood/felwood.cpp new file mode 100644 index 00000000000..4e8b6e3cdc3 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/felwood/felwood.cpp @@ -0,0 +1,89 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Felwood +SD%Complete: 95 +SDComment: Quest support: related to 4101&4102 (To obtain Cenarion Beacon) +SDCategory: Felwood +EndScriptData */ + +/* ContentData +npcs_riverbreeze_and_silversky +EndContentData */ + +#include "precompiled.h" + +/*###### +## npcs_riverbreeze_and_silversky +######*/ + +#define GOSSIP_ITEM_BEACON "Please make me a Cenarion Beacon" + +bool GossipHello_npcs_riverbreeze_and_silversky(Player *player, Creature *_Creature) +{ + uint32 eCreature = _Creature->GetEntry(); + + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( eCreature==9528 ) + { + if( player->GetQuestRewardStatus(4101) ) + { + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM_BEACON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(2848, _Creature->GetGUID()); + }else if( player->GetTeam()==HORDE ) + player->SEND_GOSSIP_MENU(2845, _Creature->GetGUID()); + else + player->SEND_GOSSIP_MENU(2844, _Creature->GetGUID()); + } + + if( eCreature==9529 ) + { + if( player->GetQuestRewardStatus(4102) ) + { + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM_BEACON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(2849, _Creature->GetGUID()); + }else if( player->GetTeam()==ALLIANCE ) + player->SEND_GOSSIP_MENU(2843, _Creature->GetGUID()); + else + player->SEND_GOSSIP_MENU(2842, _Creature->GetGUID()); + } + + return true; +} + +bool GossipSelect_npcs_riverbreeze_and_silversky(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if( action==GOSSIP_ACTION_INFO_DEF+1 ) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->CastSpell(player, 15120, false); + } + return true; +} + +void AddSC_felwood() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npcs_riverbreeze_and_silversky"; + newscript->pGossipHello = &GossipHello_npcs_riverbreeze_and_silversky; + newscript->pGossipSelect = &GossipSelect_npcs_riverbreeze_and_silversky; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/feralas/feralas.cpp b/src/bindings/scripts/scripts/zone/feralas/feralas.cpp new file mode 100644 index 00000000000..ef886eadc65 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/feralas/feralas.cpp @@ -0,0 +1,85 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Feralas +SD%Complete: 100 +SDComment: Quest support: 3520. Special vendor Gregan Brewspewer +SDCategory: Feralas +EndScriptData */ + +#include "precompiled.h" + +/*###### +## npc_gregan_brewspewer +######*/ + +bool GossipHello_npc_gregan_brewspewer(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( _Creature->isVendor() && player->GetQuestStatus(3909) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM(0, "Buy somethin', will ya?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(2433,_Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_gregan_brewspewer(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_ACTION_INFO_DEF+1 ) + { + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + player->SEND_GOSSIP_MENU(2434,_Creature->GetGUID()); + } + if( action == GOSSIP_ACTION_TRADE ) + player->SEND_VENDORLIST( _Creature->GetGUID() ); + return true; +} + +/*###### +## npc_screecher_spirit +######*/ + +bool GossipHello_npc_screecher_spirit(Player *player, Creature *_Creature) +{ + player->SEND_GOSSIP_MENU(2039,_Creature->GetGUID() ); + player->TalkedToCreature(_Creature->GetEntry(), _Creature->GetGUID()); + _Creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_feralas() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_gregan_brewspewer"; + newscript->pGossipHello = &GossipHello_npc_gregan_brewspewer; + newscript->pGossipSelect = &GossipSelect_npc_gregan_brewspewer; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_screecher_spirit"; + newscript->pGossipHello = &GossipHello_npc_screecher_spirit; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/ghostlands/ghostlands.cpp b/src/bindings/scripts/scripts/zone/ghostlands/ghostlands.cpp new file mode 100644 index 00000000000..759da44db12 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/ghostlands/ghostlands.cpp @@ -0,0 +1,134 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Ghostlands +SD%Complete: 100 +SDComment: Quest support: 9692. Obtain Budd's Guise of Zul'aman. Vendor Rathis Tomber +SDCategory: Ghostlands +EndScriptData */ + +/* ContentData +npc_blood_knight_dawnstar +npc_budd_nedreck +npc_rathis_tomber +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_blood_knight_dawnstar +######*/ + +bool GossipHello_npc_blood_knight_dawnstar(Player *player, Creature *_Creature) +{ + if (player->GetQuestStatus(9692) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(24226,1,true)) + player->ADD_GOSSIP_ITEM( 0, "Take Blood Knight Insignia", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_blood_knight_dawnstar(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + ItemPosCountVec dest; + uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, 24226, 1, false); + if( msg == EQUIP_ERR_OK ) + { + player->StoreNewItem( dest, 24226, 1, true); + player->PlayerTalkClass->ClearMenus(); + } + } + return true; +} + +/*###### +## npc_budd_nedreck +######*/ + +bool GossipHello_npc_budd_nedreck(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( player->GetQuestStatus(11166) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM(0,"You gave the crew disguises?",GOSSIP_SENDER_MAIN,GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(),_Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_budd_nedreck(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action==GOSSIP_ACTION_INFO_DEF ) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->CastSpell(player, 42540, false); + } + return true; +} + +/*###### +## npc_rathis_tomber +######*/ + +bool GossipHello_npc_rathis_tomber(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( _Creature->isVendor() && player->GetQuestRewardStatus(9152) ) + { + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + player->SEND_GOSSIP_MENU(8432,_Creature->GetGUID()); + }else + player->SEND_GOSSIP_MENU(8431,_Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_rathis_tomber(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_ACTION_TRADE ) + player->SEND_VENDORLIST( _Creature->GetGUID() ); + return true; +} + +void AddSC_ghostlands() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_blood_knight_dawnstar"; + newscript->pGossipHello = &GossipHello_npc_blood_knight_dawnstar; + newscript->pGossipSelect = &GossipSelect_npc_blood_knight_dawnstar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_budd_nedreck"; + newscript->pGossipHello = &GossipHello_npc_budd_nedreck; + newscript->pGossipSelect = &GossipSelect_npc_budd_nedreck; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_rathis_tomber"; + newscript->pGossipHello = &GossipHello_npc_rathis_tomber; + newscript->pGossipSelect = &GossipSelect_npc_rathis_tomber; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/gruuls_lair/boss_gruul.cpp b/src/bindings/scripts/scripts/zone/gruuls_lair/boss_gruul.cpp new file mode 100644 index 00000000000..d43ac25f08c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/gruuls_lair/boss_gruul.cpp @@ -0,0 +1,281 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Gruul +SD%Complete: 25 +SDComment: Ground Slam seriously messed up due to core problem +SDCategory: Gruul's Lair +EndScriptData */ + +#include "precompiled.h" +#include "def_gruuls_lair.h" + +#define SPELL_GROWTH 36300 +#define SPELL_CAVE_IN 36240 +#define SPELL_GROUND_SLAM 33525 // AoE Ground Slam applying Ground Slam to everyone with a script effect (most likely the knock back, we can code it to a set knockback) +#define SPELL_SHATTER_EFFECT 33671 +#define SPELL_HURTFUL_STRIKE 33813 +#define SPELL_REVERBERATION 36297 //AoE Silence +#define SPELL_GRONN_LORDS_GRASP 33572 //Already handled in GroundSlam +#define SPELL_STONED 33652 //-- Spell is self cast +#define SPELL_SHATTER 33654 +#define SPELL_MAGNETIC_PULL 28337 +#define SPELL_KNOCK_BACK 24199 //Knockback spell until correct implementation is made + +#define EMOTE_GROW "grows in size!" +#define SAY_AGGRO "Come.... and die." + +struct MANGOS_DLL_DECL boss_gruulAI : public ScriptedAI +{ + boss_gruulAI(Creature *c) : ScriptedAI(c) { Reset(); } + + ScriptedInstance *pInstance; + + uint32 Growth_Timer; + uint32 CaveIn_Timer; + uint32 GroundSlamTimer; + uint32 GroundSlamStage; + uint32 PerformingGroundSlam; + uint32 HurtfulStrike_Timer; + uint32 Reverberation_Timer; + + void Reset() + { + pInstance = (ScriptedInstance*)m_creature->GetInstanceData(); + + Growth_Timer= 30000; + CaveIn_Timer= 40000; + GroundSlamTimer= 35000; + GroundSlamStage= 0; + PerformingGroundSlam= false; + HurtfulStrike_Timer= 8000; + Reverberation_Timer= 60000+45000; + + if(pInstance) + pInstance->SetData(DATA_GRUULEVENT, 0); + } + + void JustDied(Unit* Killer) + { + if(pInstance) + pInstance->SetData(DATA_GRUULEVENT, 1); + } + + void Aggro(Unit *who) + { + + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + + if(pInstance) + pInstance->SetData(DATA_GRUULEVENT, 1); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + // Growth + // Gruul can cast this spell up to 30 times + if (Growth_Timer < diff) + { + DoCast(m_creature,SPELL_GROWTH); + DoTextEmote(EMOTE_GROW,NULL); + Growth_Timer = 30000; + }else Growth_Timer -= diff; + + if(PerformingGroundSlam) + { + if(GroundSlamTimer < diff) + { + switch(GroundSlamStage) + { + case 0: + { + //Begin the whole ordeal + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + + std::vector knockback_targets; + + //First limit the list to only players + for(std::list::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) + { + Unit *target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + + if(target && target->GetTypeId() == TYPEID_PLAYER) + knockback_targets.push_back(target); + } + + //Now to totally disoriend those players + for(std::vector::iterator itr = knockback_targets.begin(); itr!= knockback_targets.end(); ++itr) + { + Unit *target = *itr; + Unit *target2 = *(knockback_targets.begin() + rand()%knockback_targets.size()); + + if(target && target2) + { + switch(rand()%2) + { + case 0: target2->CastSpell(target, SPELL_MAGNETIC_PULL, true, NULL, NULL, m_creature->GetGUID()); break; + case 1: target2->CastSpell(target, SPELL_KNOCK_BACK, true, NULL, NULL, m_creature->GetGUID()); break; + } + } + } + + GroundSlamTimer = 7000; + } break; + + case 1: + { + //Players are going to get stoned + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + + for(std::list::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) + { + Unit *target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + + if(target) + { + target->RemoveAurasDueToSpell(SPELL_GRONN_LORDS_GRASP); + target->CastSpell(target, SPELL_STONED, true, NULL, NULL, m_creature->GetGUID()); + } + } + + GroundSlamTimer = 5000; + + } break; + + case 2: + { + //The dummy shatter spell is cast + DoCast(m_creature, SPELL_SHATTER); + + GroundSlamTimer = 1000; + + } break; + + case 3: + { + //Shatter takes effect + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + + for(std::list::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr) + { + Unit *target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + + if(target) + { + target->RemoveAurasDueToSpell(SPELL_STONED); + + if(target->GetTypeId() == TYPEID_PLAYER) + target->CastSpell(target, SPELL_SHATTER_EFFECT, false, NULL, NULL, m_creature->GetGUID()); + } + + } + + m_creature->GetMotionMaster()->Clear(); + + Unit *victim = m_creature->getVictim(); + if(victim) + { + m_creature->GetMotionMaster()->MoveChase(victim); + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID()); + } + + PerformingGroundSlam = false; + + GroundSlamTimer =120000; + HurtfulStrike_Timer= 8000; + if(Reverberation_Timer < 10000) //Give a little time to the players to undo the damage from shatter + Reverberation_Timer += 10000; + + } break; + } + + GroundSlamStage++; + } + else + GroundSlamTimer-=diff; + } + else + { + // Hurtful Strike + if (HurtfulStrike_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_TOPAGGRO,1); + + if (target && m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + DoCast(target,SPELL_HURTFUL_STRIKE); + else + DoCast(m_creature->getVictim(),SPELL_HURTFUL_STRIKE); + + HurtfulStrike_Timer= 8000; + }else HurtfulStrike_Timer -= diff; + + // Reverberation + if (Reverberation_Timer < diff) + { + m_creature->CastSpell(m_creature->getVictim(), SPELL_REVERBERATION, true); + + Reverberation_Timer = 30000; + }else Reverberation_Timer -= diff; + + // Cave In + if (CaveIn_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + if(target) + DoCast(target,SPELL_CAVE_IN); + + CaveIn_Timer = 20000; + }else CaveIn_Timer -= diff; + + // Ground Slam, Gronn Lord's Grasp, Stoned, Shatter + if (GroundSlamTimer < diff) + { + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0); + + PerformingGroundSlam= true; + GroundSlamTimer = 0; + GroundSlamStage = 0; + DoCast(m_creature->getVictim(), SPELL_GROUND_SLAM); + } else GroundSlamTimer -=diff; + + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_boss_gruul(Creature *_Creature) +{ + return new boss_gruulAI (_Creature); +} + +void AddSC_boss_gruul() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_gruul"; + newscript->GetAI = GetAI_boss_gruul; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/gruuls_lair/boss_high_king_maulgar.cpp b/src/bindings/scripts/scripts/zone/gruuls_lair/boss_high_king_maulgar.cpp new file mode 100644 index 00000000000..e69a46104d3 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/gruuls_lair/boss_high_king_maulgar.cpp @@ -0,0 +1,687 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_High_King_Maulgar +SD%Complete: 80 +SDComment: Verify that the script is working properly +SDCategory: Gruul's Lair +EndScriptData */ + +#include "precompiled.h" +#include "def_gruuls_lair.h" + +#define SOUND_AGGRO 11367 //"Gronn are the real power in outland." + +#define SOUND_ENRAGE 11368 //"You will not defeat the hand of Gruul!" + +#define SOUND_OGRE_DEATH1 11369 //"You won't kill next one so easy!" +#define SOUND_OGRE_DEATH2 11370 //"Pah! Does not prove anything!" +#define SOUND_OGRE_DEATH3 11371 //"I'm not afraid of you." +#define SOUND_OGRE_DEATH4 11372 //"Good, now you fight me!" + +#define SOUND_SLAY1 11373 //"You not so tough afterall!" +#define SOUND_SLAY2 11374 //"Aha ha ha ha!" +#define SOUND_SLAY3 11375 //"Mulgar is king!" + +#define SOUND_DEATH 11376 //"Gruul ...will crush you..." + +// High King Maulgar +#define SPELL_ARCING_SMASH 39144 +#define SPELL_MIGHTY_BLOW 33230 +#define SPELL_WHIRLWIND 33238 +#define SPELL_ENRAGE 34970 + +// Council spells +#define SPELL_DARK_DECAY 33129 +#define SPELL_GREATER_POLYMORPH 33173 +#define SPELL_LIGHTNING_BOLT 36152 +#define SPELL_ARCANE_SHOCK 33175 +#define SPELL_ARCANE_EXPLOSION 33237 +#define SPELL_GREATER_PW_SHIELD 33147 +#define SPELL_HEAL 33144 +#define SPELL_GREATER_FIREBALL 33051 +#define SPELL_SPELLSHIELD 33054 +#define SPELL_BLAST_WAVE 33061 + +//High King Maulgar AI +struct MANGOS_DLL_DECL boss_high_king_maulgarAI : public ScriptedAI +{ + boss_high_king_maulgarAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + for(uint8 i = 0; i < 4; ++i) + Council[i] = 0; + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 ArcingSmash_Timer; + uint32 MightyBlow_Timer; + uint32 Whirlwind_Timer; + uint32 Charging_Timer; + + bool Phase2; + + uint64 Council[4]; + + void Reset() + { + ArcingSmash_Timer = 10000; + MightyBlow_Timer = 40000; + Whirlwind_Timer = 30000; + Charging_Timer = 0; + Phase2 = false; + + Creature *pCreature = NULL; + for(uint8 i = 0; i < 4; i++) + { + if(Council[i]) + { + pCreature = (Creature*)(Unit::GetUnit((*m_creature), Council[i])); + if(pCreature && !pCreature->isAlive()) + { + pCreature->Respawn(); + pCreature->AI()->EnterEvadeMode(); + } + } + } + + //reset encounter + if (pInstance) + pInstance->SetData(DATA_MAULGAREVENT, 0); + } + + void KilledUnit() + { + switch(rand()%2) + { + case 0: DoPlaySoundToSet(m_creature, SOUND_SLAY1); break; + case 1: DoPlaySoundToSet(m_creature, SOUND_SLAY2); break; + case 2: DoPlaySoundToSet(m_creature, SOUND_SLAY3); break; + } + } + + void JustDied(Unit* Killer) + { + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if (pInstance) + pInstance->SetData(DATA_MAULGAREVENT, 0); + } + + void Aggro(Unit *who) { StartEvent(who); } + + void GetCouncil() + { + //get council member's guid to respawn them if needed + Council[0] = pInstance->GetData64(DATA_KIGGLERTHECRAZED); + Council[1] = pInstance->GetData64(DATA_BLINDEYETHESEER); + Council[2] = pInstance->GetData64(DATA_OLMTHESUMMONER); + Council[3] = pInstance->GetData64(DATA_KROSHFIREHAND); + } + + void StartEvent(Unit *who) + { + if(!pInstance) + return; + + GetCouncil(); + + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + + pInstance->SetData64(DATA_MAULGAREVENT_TANK, who->GetGUID()); + pInstance->SetData(DATA_MAULGAREVENT, 1); + } + + void UpdateAI(const uint32 diff) + { + //Only if not incombat check if the event is started + if(!InCombat && pInstance && pInstance->GetData(DATA_MAULGAREVENT)) + { + Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MAULGAREVENT_TANK)); + + if(target) + { + DoStartAttackAndMovement(target); + + GetCouncil(); + + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //someone evaded! + if(pInstance && !pInstance->GetData(DATA_MAULGAREVENT)) + EnterEvadeMode(); + + //ArcingSmash_Timer + if (ArcingSmash_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ARCING_SMASH); + ArcingSmash_Timer = 10000; + }else ArcingSmash_Timer -= diff; + + //Whirlwind_Timer + if (Whirlwind_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_WHIRLWIND); + Whirlwind_Timer = 55000; + }else Whirlwind_Timer -= diff; + + //MightyBlow_Timer + if (MightyBlow_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_MIGHTY_BLOW); + MightyBlow_Timer = 30000+rand()%10000; + }else MightyBlow_Timer -= diff; + + //Entering Phase 2 + if(!Phase2 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 50) + { + Phase2 = true; + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + } + + if(Phase2) + { + //Charging_Timer + if(Charging_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + DoStartAttackAndMovement(target); + + Charging_Timer = 20000; + }else Charging_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; + +//Olm The Summoner AI +struct MANGOS_DLL_DECL boss_olm_the_summonerAI : public ScriptedAI +{ + boss_olm_the_summonerAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + uint32 DarkDecay_Timer; + uint32 Summon_Timer; + + ScriptedInstance* pInstance; + + void Reset() + { + DarkDecay_Timer = 10000; + Summon_Timer = 15000; + + //reset encounter + if (pInstance) + pInstance->SetData(DATA_MAULGAREVENT, 0); + } + + void Aggro(Unit *who) + { + if(pInstance) + { + pInstance->SetData64(DATA_MAULGAREVENT_TANK, who->GetGUID()); + pInstance->SetData(DATA_MAULGAREVENT, 1); + } + } + + float DoCalculateRandomLocation() + { + float Loc; + float Rand = rand()%8; + + switch(rand()%2) + { + case 0: Loc = 0 + Rand; break; + case 1: Loc = 0 - Rand; break; + } + return Loc; + } + + void UpdateAI(const uint32 diff) + { + //Only if not incombat check if the event is started + if(!InCombat && pInstance && pInstance->GetData(DATA_MAULGAREVENT)) + { + Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MAULGAREVENT_TANK)); + + if(target) + { + DoStartAttackAndMovement(target); + } + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //someone evaded! + if(pInstance && !pInstance->GetData(DATA_MAULGAREVENT)) + EnterEvadeMode(); + + //DarkDecay_Timer + if(DarkDecay_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_DARK_DECAY); + DarkDecay_Timer = 20000; + }else DarkDecay_Timer -= diff; + + //Summon_Timer + if(Summon_Timer < diff) + { + Creature *Add = NULL; + Add = DoSpawnCreature(18847, DoCalculateRandomLocation(), DoCalculateRandomLocation(), 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + Summon_Timer = 30000; + }else Summon_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Kiggler The Crazed AI +struct MANGOS_DLL_DECL boss_kiggler_the_crazedAI : public ScriptedAI +{ + boss_kiggler_the_crazedAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + uint32 GreatherPolymorph_Timer; + uint32 LightningBolt_Timer; + uint32 ArcaneShock_Timer; + uint32 ArcaneExplosion_Timer; + + ScriptedInstance* pInstance; + + void Reset() + { + GreatherPolymorph_Timer = 5000; + LightningBolt_Timer = 10000; + ArcaneShock_Timer = 20000; + ArcaneExplosion_Timer = 30000; + + //reset encounter + if (pInstance) + pInstance->SetData(DATA_MAULGAREVENT, 0); + } + + void Aggro(Unit *who) + { + if(pInstance) + { + pInstance->SetData64(DATA_MAULGAREVENT_TANK, who->GetGUID()); + pInstance->SetData(DATA_MAULGAREVENT, 1); + } + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + if(!InCombat) + { + DoStartAttackAndMovement(who); + if(pInstance) + { + pInstance->SetData64(DATA_MAULGAREVENT_TANK, who->GetGUID()); + pInstance->SetData(DATA_MAULGAREVENT, 1); + } + } + } + } + } + + void UpdateAI(const uint32 diff) + { + //Only if not incombat check if the event is started + if(!InCombat && pInstance && pInstance->GetData(DATA_MAULGAREVENT)) + { + Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MAULGAREVENT_TANK)); + + if(target) + { + DoStartAttackAndMovement(target); + } + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //someone evaded! + if(pInstance && !pInstance->GetData(DATA_MAULGAREVENT)) + EnterEvadeMode(); + + //GreaterPolymorph_Timer / disabled: it makes you fall under the texture / if you've got vmaps feel free to uncomment this + /*if(GreaterPolymorph_Timer < diff) + { + Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + DoCast(target, SPELL_GREATER_POLYMORPH); + + GreaterPolymorph_Timer = 20000; + }else GreaterPolymorph_Timer -= diff;*/ + + //LightningBolt_Timer + if(LightningBolt_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_LIGHTNING_BOLT); + LightningBolt_Timer = 15000; + }else LightningBolt_Timer -= diff; + + //ArcaneShock_Timer + if(ArcaneShock_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ARCANE_SHOCK); + ArcaneShock_Timer = 20000; + }else ArcaneShock_Timer -= diff; + + //ArcaneExplosion_Timer + if(ArcaneExplosion_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ARCANE_EXPLOSION); + ArcaneExplosion_Timer = 30000; + }else ArcaneExplosion_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Blindeye The Seer AI +struct MANGOS_DLL_DECL boss_blindeye_the_seerAI : public ScriptedAI +{ + boss_blindeye_the_seerAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + uint32 GreaterPowerWordShield_Timer; + uint32 Heal_Timer; + + ScriptedInstance* pInstance; + + void Reset() + { + GreaterPowerWordShield_Timer = 5000; + Heal_Timer = 30000; + + //reset encounter + if (pInstance) + pInstance->SetData(DATA_MAULGAREVENT, 0); + } + + void Aggro(Unit *who) + { + if(pInstance) + { + pInstance->SetData64(DATA_MAULGAREVENT_TANK, who->GetGUID()); + pInstance->SetData(DATA_MAULGAREVENT, 1); + } + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + if(!InCombat) + { + DoStartAttackAndMovement(who); + if(pInstance) + { + pInstance->SetData64(DATA_MAULGAREVENT_TANK, who->GetGUID()); + pInstance->SetData(DATA_MAULGAREVENT, 1); + } + } + } + } + } + + void UpdateAI(const uint32 diff) + { + //Only if not incombat check if the event is started + if(!InCombat && pInstance && pInstance->GetData(DATA_MAULGAREVENT)) + { + Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MAULGAREVENT_TANK)); + + if(target) + { + DoStartAttackAndMovement(target); + } + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //someone evaded! + if(pInstance && !pInstance->GetData(DATA_MAULGAREVENT)) + EnterEvadeMode(); + + //GreaterPowerWordShield_Timer + if(GreaterPowerWordShield_Timer < diff) + { + DoCast(m_creature, SPELL_GREATER_PW_SHIELD); + GreaterPowerWordShield_Timer = 40000; + }else GreaterPowerWordShield_Timer -= diff; + + //Heal_Timer + if(Heal_Timer < diff) + { + DoCast(m_creature, SPELL_HEAL); + Heal_Timer = 60000; + }else Heal_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Krosh Firehand AI +struct MANGOS_DLL_DECL boss_krosh_firehandAI : public ScriptedAI +{ + boss_krosh_firehandAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + uint32 GreaterFireball_Timer; + uint32 SpellShield_Timer; + uint32 BlastWave_Timer; + + ScriptedInstance* pInstance; + + void Reset() + { + GreaterFireball_Timer = 1000; + SpellShield_Timer = 5000; + BlastWave_Timer = 20000; + + //reset encounter + if (pInstance) + pInstance->SetData(DATA_MAULGAREVENT, 0); + } + + void Aggro(Unit *who) + { + if(pInstance) + { + pInstance->SetData64(DATA_MAULGAREVENT_TANK, who->GetGUID()); + pInstance->SetData(DATA_MAULGAREVENT, 1); + } + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + if(!InCombat) + { + DoStartAttackAndMovement(who); + if(pInstance) + { + pInstance->SetData64(DATA_MAULGAREVENT_TANK, who->GetGUID()); + pInstance->SetData(DATA_MAULGAREVENT, 1); + } + } + } + } + } + + void UpdateAI(const uint32 diff) + { + //Only if not incombat check if the event is started + if(!InCombat && pInstance && pInstance->GetData(DATA_MAULGAREVENT)) + { + Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MAULGAREVENT_TANK)); + + if(target) + { + DoStartAttackAndMovement(target); + } + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //someone evaded! + if(pInstance && !pInstance->GetData(DATA_MAULGAREVENT)) + EnterEvadeMode(); + + //GreaterFireball_Timer + if(GreaterFireball_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_GREATER_FIREBALL); + GreaterFireball_Timer = 2000; + }else GreaterFireball_Timer -= diff; + + //SpellShield_Timer + if(SpellShield_Timer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature->getVictim(), SPELL_SPELLSHIELD); + SpellShield_Timer = 30000; + }else SpellShield_Timer -= diff; + + //BlastWave_Timer + if(BlastWave_Timer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature->getVictim(), SPELL_BLAST_WAVE); + BlastWave_Timer = 60000; + }else BlastWave_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_high_king_maulgar(Creature *_Creature) +{ + return new boss_high_king_maulgarAI (_Creature); +} + +CreatureAI* GetAI_boss_olm_the_summoner(Creature *_Creature) +{ + return new boss_olm_the_summonerAI (_Creature); +} + +CreatureAI *GetAI_boss_kiggler_the_crazed(Creature *_Creature) +{ + return new boss_kiggler_the_crazedAI (_Creature); +} + +CreatureAI *GetAI_boss_blindeye_the_seer(Creature *_Creature) +{ + return new boss_blindeye_the_seerAI (_Creature); +} + +CreatureAI *GetAI_boss_krosh_firehand(Creature *_Creature) +{ + return new boss_krosh_firehandAI (_Creature); +} + +void AddSC_boss_high_king_maulgar() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_high_king_maulgar"; + newscript->GetAI = GetAI_boss_high_king_maulgar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_kiggler_the_crazed"; + newscript->GetAI = GetAI_boss_kiggler_the_crazed; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_blindeye_the_seer"; + newscript->GetAI = GetAI_boss_blindeye_the_seer; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_olm_the_summoner"; + newscript->GetAI = GetAI_boss_olm_the_summoner; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_krosh_firehand"; + newscript->GetAI = GetAI_boss_krosh_firehand; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/gruuls_lair/def_gruuls_lair.h b/src/bindings/scripts/scripts/zone/gruuls_lair/def_gruuls_lair.h new file mode 100644 index 00000000000..e2d780dabda --- /dev/null +++ b/src/bindings/scripts/scripts/zone/gruuls_lair/def_gruuls_lair.h @@ -0,0 +1,15 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_GRUULS_LAIR_H +#define DEF_GRUULS_LAIR_H + +#define DATA_BLINDEYETHESEER 1 +#define DATA_GRUULEVENT 2 +#define DATA_KIGGLERTHECRAZED 3 +#define DATA_KROSHFIREHAND 4 +#define DATA_MAULGAREVENT 5 +#define DATA_MAULGAREVENT_TANK 6 +#define DATA_OLMTHESUMMONER 7 +#endif diff --git a/src/bindings/scripts/scripts/zone/gruuls_lair/instance_gruuls_lair.cpp b/src/bindings/scripts/scripts/zone/gruuls_lair/instance_gruuls_lair.cpp new file mode 100644 index 00000000000..158af022905 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/gruuls_lair/instance_gruuls_lair.cpp @@ -0,0 +1,139 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Gruuls_Lair +SD%Complete: 100 +SDComment: +SDCategory: Gruul's Lair +EndScriptData */ + +#include "precompiled.h" +#include "def_gruuls_lair.h" + +#define ENCOUNTERS 2 + +/* Gruuls Lair encounters: +1 - High King Maulgar event +2 - Gruul event +*/ + +struct MANGOS_DLL_DECL instance_gruuls_lair : public ScriptedInstance +{ + instance_gruuls_lair(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + bool Encounters[ENCOUNTERS]; + + uint64 MaulgarEvent_Tank; + uint64 KigglerTheCrazed; + uint64 BlindeyeTheSeer; + uint64 OlmTheSummoner; + uint64 KroshFirehand; + + void Initialize() + { + MaulgarEvent_Tank = 0; + KigglerTheCrazed = 0; + BlindeyeTheSeer = 0; + OlmTheSummoner = 0; + KroshFirehand = 0; + + for(uint8 i = 0; i < ENCOUNTERS; i++) + Encounters[i] = false; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < ENCOUNTERS; i++) + if(Encounters[i]) return true; + + return false; + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case 18835: KigglerTheCrazed = creature->GetGUID(); break; + case 18836: BlindeyeTheSeer = creature->GetGUID(); break; + case 18834: OlmTheSummoner = creature->GetGUID(); break; + case 18832: KroshFirehand = creature->GetGUID(); break; + } + } + + void SetData64(uint32 type, uint64 data) + { + if(type == DATA_MAULGAREVENT_TANK) + MaulgarEvent_Tank = data; + } + + uint64 GetData64(uint32 identifier) + { + switch(identifier) + { + case DATA_MAULGAREVENT_TANK: + return MaulgarEvent_Tank; + case DATA_KIGGLERTHECRAZED: + return KigglerTheCrazed; + case DATA_BLINDEYETHESEER: + return BlindeyeTheSeer; + case DATA_OLMTHESUMMONER: + return OlmTheSummoner; + case DATA_KROSHFIREHAND: + return KroshFirehand; + } + return 0; + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_MAULGAREVENT: + Encounters[0] = (data) ? true : false; + break; + case DATA_GRUULEVENT: + Encounters[1] = (data) ? true : false; + break; + } + } + + uint32 GetData(uint32 type) + { + switch(type) + { + case DATA_MAULGAREVENT: + return Encounters[0]; + case DATA_GRUULEVENT: + return Encounters[1]; + } + return 0; + } +}; + +InstanceData* GetInstanceData_instance_gruuls_lair(Map* map) +{ + return new instance_gruuls_lair(map); +} + +void AddSC_instance_gruuls_lair() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_gruuls_lair"; + newscript->GetInstanceData = GetInstanceData_instance_gruuls_lair; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_broggok.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_broggok.cpp new file mode 100644 index 00000000000..86c300ff57a --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_broggok.cpp @@ -0,0 +1,132 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Broggok +SD%Complete: 100 +SDComment: +SDCategory: Hellfire Citadel, Blood Furnace +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SLIME_SPRAY 30913 +#define SPELL_POISON_CLOUD 30916 +#define SPELL_POISON_BOLT 30917 + +#define SPELL_POISON 30914 + +#define SAY_AGGRO "Come intruders...." + +struct MANGOS_DLL_DECL boss_broggokAI : public ScriptedAI +{ + boss_broggokAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 AcidSpray_Timer; + uint32 PoisonSpawn_Timer; + uint32 PoisonBolt_Timer; + + void Reset() + { + AcidSpray_Timer = 10000; + PoisonSpawn_Timer = 5000; + PoisonBolt_Timer = 7000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + } + + void UpdateAI(const uint32 diff) + { + + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(AcidSpray_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SLIME_SPRAY); + AcidSpray_Timer = 4000+rand()%8000; + }else AcidSpray_Timer -=diff; + + if(PoisonBolt_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_POISON_BOLT); + PoisonBolt_Timer = 4000+rand()%8000; + }else PoisonBolt_Timer -=diff; + + if(PoisonSpawn_Timer < diff) + { + DoCast(m_creature,SPELL_POISON_CLOUD); + PoisonSpawn_Timer = 20000; + }else PoisonSpawn_Timer -=diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_broggok_poisoncloudAI : public ScriptedAI +{ + mob_broggok_poisoncloudAI(Creature *c) : ScriptedAI(c) {Reset();} + + bool Start; + + void Reset() + { + Start = false; + } + + void Aggro(Unit* who) + { + } + + void UpdateAI(const uint32 diff) + { + if(!Start) + { + m_creature->SetUInt32Value(UNIT_NPC_FLAGS,0); + m_creature->setFaction(45); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Start = true; + DoCast(m_creature,SPELL_POISON); + } + } +}; + +CreatureAI* GetAI_boss_broggokAI(Creature *_Creature) +{ + return new boss_broggokAI (_Creature); +} + +CreatureAI* GetAI_mob_broggok_poisoncloudAI(Creature *_Creature) +{ + return new mob_broggok_poisoncloudAI (_Creature); +} + +void AddSC_boss_broggok() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_broggok"; + newscript->GetAI = GetAI_boss_broggokAI; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_broggok_poisoncloud"; + newscript->GetAI = GetAI_mob_broggok_poisoncloudAI; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp new file mode 100644 index 00000000000..bf77b23d0b3 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp @@ -0,0 +1,253 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Kelidan_The_Breaker +SD%Complete: 60 +SDComment: Event with channeleres vs. boss not implemented yet +SDCategory: Hellfire Citadel, Blood Furnace +EndScriptData */ + +/* ContentData +boss_kelidan_the_breaker +mob_shadowmoon_channeler +EndContentData */ + +#include "precompiled.h" + +#define SAY_WAKE "Who dares interrupt... What is this? What have you done? You ruin everything!" +#define SOUND_WAKE 10164 + +#define SAY_ADD_AGGRO_1 "You mustn't let him loose!" +#define SOUND_ADD_AGGRO_1 10166 +#define SAY_ADD_AGGRO_2 "Ignorant whelps!" +#define SOUND_ADD_AGGRO_2 10167 +#define SAY_ADD_AGGRO_3 "You fools! He'll kill us all!" +#define SOUND_ADD_AGGRO_3 10168 + +#define SAY_KILL_1 "Just as you deserve!" +#define SOUND_KILL_1 10169 +#define SAY_KILL_2 "Your friends will soon be joining you." +#define SOUND_KILL_2 10170 + +#define SAY_NOVA "Closer... Come closer.. and burn!" +#define SOUND_NOVA 10165 + +#define SAY_DIE "Good luck... you'll need it.." +#define SOUND_DIE 10171 + +#define SPELL_CORRUPTION 30938 + +#define SPELL_FIRE_NOVA 33775 +#define H_SPELL_FIRE_NOVA 37371 + +#define SPELL_SHADOW_BOLT_VOLLEY 17228 +#define H_SPELL_SHADOW_BOLT_VOLLEY 40070 + +#define SPELL_BURNING_NOVA 30940 +#define SPELL_VORTEX 37370 + +struct MANGOS_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI +{ + boss_kelidan_the_breakerAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + HeroicMode = m_creature->GetMap()->IsHeroic(); + Reset(); + } + + ScriptedInstance* pInstance; + bool HeroicMode; + + uint32 ShadowVolley_Timer; + uint32 BurningNova_Timer; + uint32 Firenova_Timer; + uint32 Corruption_Timer; + bool Firenova; + + void Reset() + { + ShadowVolley_Timer = 1000; + BurningNova_Timer = 15000; + Corruption_Timer = 5000; + Firenova = false; + } + + void Aggro(Unit *who) + { + DoYell(SAY_WAKE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_WAKE); + } + + void KilledUnit(Unit* victim) + { + if (rand()%2) + return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_1); + break; + case 1: + DoYell(SAY_KILL_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DIE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DIE); + } + + void UpdateAI(const uint32 diff) + { + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( Firenova ) + { + if( Firenova_Timer < diff ) + { + DoCast(m_creature,HeroicMode ? H_SPELL_FIRE_NOVA : SPELL_FIRE_NOVA); + Firenova = false; + ShadowVolley_Timer = 2000; + }else Firenova_Timer -=diff; + + return; + } + + if( ShadowVolley_Timer < diff ) + { + DoCast(m_creature,HeroicMode ? H_SPELL_SHADOW_BOLT_VOLLEY : SPELL_SHADOW_BOLT_VOLLEY); + ShadowVolley_Timer = 5000+rand()%8000; + }else ShadowVolley_Timer -=diff; + + if( Corruption_Timer < diff ) + { + DoCast(m_creature,SPELL_CORRUPTION); + Corruption_Timer = 30000+rand()%20000; + }else Corruption_Timer -=diff; + + if( BurningNova_Timer < diff ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(true); + + DoYell(SAY_NOVA, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_NOVA); + + if( HeroicMode ) + DoCast(m_creature,SPELL_VORTEX); + + DoCast(m_creature,SPELL_BURNING_NOVA); + + BurningNova_Timer = 20000+rand()%8000; + Firenova_Timer= 5000; + Firenova = true; + }else BurningNova_Timer -=diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_kelidan_the_breaker(Creature *_Creature) +{ + return new boss_kelidan_the_breakerAI (_Creature); +} + +/*###### +## mob_shadowmoon_channeler +######*/ + +#define SPELL_SHADOW_BOLT 12739 +#define H_SPELL_SHADOW_BOLT 15472 + +#define SPELL_MARK_OF_SHADOW 30937 + +#define SPELL_CHANNELING 0 //initial spell channeling boss/each other not known + //when engaged all channelers must stop, trigger yell (SAY_ADD_AGGRO_*), and engage. + +struct MANGOS_DLL_DECL mob_shadowmoon_channelerAI : public ScriptedAI +{ + mob_shadowmoon_channelerAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + HeroicMode = m_creature->GetMap()->IsHeroic(); + Reset(); + } + + ScriptedInstance* pInstance; + bool HeroicMode; + + uint32 ShadowBolt_Timer; + uint32 MarkOfShadow_Timer; + + void Reset() + { + ShadowBolt_Timer = 1000+rand()%1000; + MarkOfShadow_Timer = 5000+rand()%2000; + } + + void Aggro(Unit* who) + { + //trigger boss to yell + } + + void UpdateAI(const uint32 diff) + { + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( MarkOfShadow_Timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0) ) + DoCast(target,SPELL_MARK_OF_SHADOW); + MarkOfShadow_Timer = 15000+rand()%5000; + }else MarkOfShadow_Timer -=diff; + + if( ShadowBolt_Timer < diff ) + { + DoCast(m_creature->getVictim(),HeroicMode ? H_SPELL_SHADOW_BOLT : SPELL_SHADOW_BOLT); + ShadowBolt_Timer = 5000+rand()%1000; + }else ShadowBolt_Timer -=diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_shadowmoon_channeler(Creature *_Creature) +{ + return new mob_shadowmoon_channelerAI (_Creature); +} + +void AddSC_boss_kelidan_the_breaker() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_kelidan_the_breaker"; + newscript->GetAI = GetAI_boss_kelidan_the_breaker; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_shadowmoon_channeler"; + newscript->GetAI = GetAI_mob_shadowmoon_channeler; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_the_maker.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_the_maker.cpp new file mode 100644 index 00000000000..fa5d22e33d1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/blood_furnace/boss_the_maker.cpp @@ -0,0 +1,160 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_The_Maker +SD%Complete: 80 +SDComment: Mind control no support +SDCategory: Hellfire Citadel, Blood Furnace +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_ACID_SPRAY 38153 // heroic 38973 ??? 38153 +#define SPELL_EXPLODING_BREAKER 30925 +#define SPELL_KNOCKDOWN 20276 +#define SPELL_DOMINATION 25772 // ??? + +#define SAY_KILL_1 "Let's see what I can make of you." +#define SOUND_KILL_1 10289 +#define SAY_KILL_2 "It is pointless to resist." +#define SOUND_KILL_2 10290 + +#define SAY_AGGRO_1 "My work must not be interrupted." +#define SOUND_AGGRO_1 10286 +#define SAY_AGGRO_2 "Perhaps I can find a use for you." +#define SOUND_AGGRO_2 10287 +#define SAY_AGGRO_3 "Anger... Hate... These are tools I can use." +#define SOUND_AGGRO_3 10288 + +#define SAY_DIE "Stay away from... me." +#define SOUND_DIE 10291 + +struct MANGOS_DLL_DECL boss_the_makerAI : public ScriptedAI +{ + boss_the_makerAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 AcidSpray_Timer; + uint32 ExplodingBreaker_Timer; + uint32 Domination_Timer; + uint32 Knockdown_Timer; + + void Reset() + { + AcidSpray_Timer = 10000; + ExplodingBreaker_Timer = 4000; + Domination_Timer = 120000; + Knockdown_Timer = 6000; + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + break; + case 1: + DoYell(SAY_AGGRO_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + break; + case 2: + DoYell(SAY_AGGRO_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + break; + } + } + + void KilledUnit(Unit* victim) + { + if (rand()%5) + return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_1); + break; + case 1: + DoYell(SAY_KILL_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DIE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DIE); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(AcidSpray_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ACID_SPRAY); + AcidSpray_Timer = 16000+rand()%8000; + }else AcidSpray_Timer -=diff; + + if(ExplodingBreaker_Timer < diff) + { + Unit* target; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(target,SPELL_EXPLODING_BREAKER); + ExplodingBreaker_Timer = 4000+rand()%8000; + }else ExplodingBreaker_Timer -=diff; + + /* // Disabled until Core Support for mind control + if(domination_timer_timer < diff) + { + Unit* target; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(target,SPELL_DOMINATION); + + domination_timer = 120000; + }else domination_timer -=diff; + */ + + if(Knockdown_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCKDOWN); + Knockdown_Timer = 4000+rand()%8000; + }else Knockdown_Timer -=diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_the_makerAI(Creature *_Creature) +{ + return new boss_the_makerAI (_Creature); +} + +void AddSC_boss_the_maker() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_the_maker"; + newscript->GetAI = GetAI_boss_the_makerAI; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp new file mode 100644 index 00000000000..970a1774683 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp @@ -0,0 +1,277 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Omar_The_Unscarred +SD%Complete: 90 +SDComment: Normal/Heroic support: both, to be tested. Temporary solution for orbital/shadow whip-ability. Needs more core support before making it more proper. +SDCategory: Hellfire Citadel, Hellfire Ramparts +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_ORBITAL_STRIKE 30637 +#define SPELL_SHADOW_WHIP 30638 +#define SPELL_TREACHEROUS_AURA 30695 +#define H_SPELL_BANE_OF_TREACHERY 37566 +#define SPELL_DEMONIC_SHIELD 31901 +#define SPELL_SHADOW_BOLT 30686 +#define H_SPELL_SHADOW_BOLT 39297 +#define SPELL_SUMMON_FIENDISH_HOUND 30707 + +#define SAY_AGGRO_1 "You dare stand against me?!" +#define SOUND_AGGRO_1 10280 +#define SAY_AGGRO_2 "I will not be defeated!" +#define SOUND_AGGRO_2 10279 +#define SAY_AGGRO_3 "Your insolence will be your death." +#define SOUND_AGGRO_3 10281 + +#define SAY_SUMMON "Achor-she-ki! Feast my pet! Eat your fill!" +#define SOUND_SUMMON 10277 + +#define SAY_CURSE "A-Kreesh!" +#define SOUND_CURSE 10278 + +#define SAY_KILL_1 "Die, weakling!" +#define SOUND_KILL_1 10282 + +#define SAY_DIE "It is... not over." +#define SOUND_DIE 10284 + +#define SAY_WIPE "I am victorious!" +#define SOUND_WIPE 10283 + +struct MANGOS_DLL_DECL boss_omor_the_unscarredAI : public ScriptedAI +{ + boss_omor_the_unscarredAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 OrbitalStrike_Timer; + uint32 ShadowWhip_Timer; + uint32 Aura_Timer; + uint32 DemonicShield_Timer; + uint32 Shadowbolt_Timer; + uint32 Summon_Timer; + uint32 SummonedCount; + uint64 playerGUID; + bool CanPullBack; + bool HeroicMode; + + void Reset() + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + + DoYell(SAY_WIPE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_WIPE); + + OrbitalStrike_Timer = 25000; + ShadowWhip_Timer = 2000; + Aura_Timer = 10000; + DemonicShield_Timer = 1000; + Shadowbolt_Timer = 2000; + Summon_Timer = 10000; + SummonedCount = 0; + playerGUID = 0; + CanPullBack = false; + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + break; + case 1: + DoYell(SAY_AGGRO_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + break; + case 2: + DoYell(SAY_AGGRO_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + break; + } + } + + void AttackStart(Unit* who) + { + if (!who) + return; + + if (who->isTargetableForAttack()) + { + DoStartAttackNoMovement(who); + + if (!InCombat) + { + Aggro(who); + InCombat = true; + } + } + } + + void MoveInLineOfSight(Unit* who) + { + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if (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) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void KilledUnit(Unit* victim) + { + if (rand()%2) + return; + + DoYell(SAY_KILL_1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_1); + } + + void JustSummoned(Creature* summoned) + { + DoYell(SAY_SUMMON,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SUMMON); + + if( Unit* random = SelectUnit(SELECT_TARGET_RANDOM,0) ) + summoned->AI()->AttackStart(random); + + ++SummonedCount; + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DIE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DIE); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //only two may be wrong, perhaps increase timer and spawn periodically instead. + if( SummonedCount < 2 ) + { + if( Summon_Timer < diff ) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature,SPELL_SUMMON_FIENDISH_HOUND); + Summon_Timer = 15000+rand()%15000; + }else Summon_Timer -= diff; + } + + if( CanPullBack ) + { + if( ShadowWhip_Timer < diff ) + { + if( Unit* temp = Unit::GetUnit(*m_creature,playerGUID) ) + { + //if unit dosen't have this flag, then no pulling back (script will attempt cast, even if orbital strike was resisted) + if( temp->HasUnitMovementFlag(MOVEMENTFLAG_FALLING) ) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(temp,SPELL_SHADOW_WHIP); + } + } + playerGUID = 0; + ShadowWhip_Timer = 2000; + CanPullBack = false; + }else ShadowWhip_Timer -= diff; + } + else if( OrbitalStrike_Timer < diff ) + { + Unit* temp = NULL; + if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE) ) + temp = m_creature->getVictim(); + else temp = SelectUnit(SELECT_TARGET_RANDOM,0); + + if( temp && temp->GetTypeId() == TYPEID_PLAYER ) + { + DoCast(temp,SPELL_ORBITAL_STRIKE); + OrbitalStrike_Timer = 14000+rand()%2000; + playerGUID = temp->GetGUID(); + if( playerGUID ) + CanPullBack = true; + } + }else OrbitalStrike_Timer -= diff; + + if( (m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 20 ) + { + if( DemonicShield_Timer < diff ) + { + DoCast(m_creature,SPELL_DEMONIC_SHIELD); + DemonicShield_Timer = 15000; + }else DemonicShield_Timer -= diff; + } + + if( Aura_Timer < diff ) + { + DoYell(SAY_CURSE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_CURSE); + + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + { + if( HeroicMode ) DoCast(target,H_SPELL_BANE_OF_TREACHERY); + else DoCast(target,SPELL_TREACHEROUS_AURA); + + Aura_Timer = 8000+rand()%8000; + } + }else Aura_Timer -= diff; + + if( Shadowbolt_Timer < diff ) + { + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + { + if( target ) + target = m_creature->getVictim(); + + if( HeroicMode ) DoCast(target,H_SPELL_SHADOW_BOLT); + else DoCast(target,SPELL_SHADOW_BOLT); + + Shadowbolt_Timer = 4000+rand()%2500; + } + }else Shadowbolt_Timer -= diff; + } +}; + +CreatureAI* GetAI_boss_omor_the_unscarredAI(Creature *_Creature) +{ + return new boss_omor_the_unscarredAI (_Creature); +} + +void AddSC_boss_omor_the_unscarred() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_omor_the_unscarred"; + newscript->GetAI = GetAI_boss_omor_the_unscarredAI; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp new file mode 100644 index 00000000000..247af6545fd --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp @@ -0,0 +1,200 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Watchkeeper_Gargolmar +SD%Complete: 80 +SDComment: Normal/Heroic support: both, to be tested. Missing adds to heal him. Surge should be used on target furthest away, not random. +SDCategory: Hellfire Citadel, Hellfire Ramparts +EndScriptData */ + +#include "precompiled.h" + +#define SAY_HEAL "Heal me! QUICKLY!" +#define SOUND_HEAL 10329 + +#define SAY_SURGE "Back off, pup!" +#define SOUND_SURGE 10330 + +#define SAY_TAUNT "Do you smell that? Fresh meat has somehow breached our citadel. Be wary of any intruders." + +#define SAY_AGGRO_1 "What have we here...?" +#define SOUND_AGGRO_1 10331 +#define SAY_AGGRO_2 "Heh... this may hurt a little." +#define SOUND_AGGRO_2 10332 +#define SAY_AGGRO_3 "I'm gonna enjoy this." +#define SOUND_AGGRO_3 10333 + +#define SAY_KILL_1 "Say farewell!" +#define SOUND_KILL_1 10334 +#define SAY_KILL_2 "Much too easy..." +#define SOUND_KILL_2 10335 + +#define SOUND_DIE 10336 + +#define SPELL_MORTAL_WOUND 30641 +#define H_SPELL_MORTAL_WOUND 36814 +#define SPELL_SURGE 34645 +#define SPELL_RETALIATION 22857 + +struct MANGOS_DLL_DECL boss_watchkeeper_gargolmarAI : public ScriptedAI +{ + boss_watchkeeper_gargolmarAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Surge_Timer; + uint32 MortalWound_Timer; + uint32 Retaliation_Timer; + + bool HasTaunted; + bool YelledForHeal; + bool HeroicMode; + + void Reset() + { + HeroicMode = m_creature->GetMap()->IsHeroic(); + + Surge_Timer = 5000; + MortalWound_Timer = 4000; + Retaliation_Timer = 0; + + HasTaunted = false; + YelledForHeal = false; + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + break; + case 1: + DoYell(SAY_AGGRO_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + break; + case 2: + DoYell(SAY_AGGRO_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + break; + } + } + + void MoveInLineOfSight(Unit* who) + { + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if (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) + { + Aggro(who); + InCombat = true; + } + } + else if (!HasTaunted && m_creature->IsWithinDistInMap(who, 60.0f)) + { + DoYell(SAY_TAUNT, LANG_UNIVERSAL, NULL); + HasTaunted = true; + } + } + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_1); + break; + case 1: + DoYell(SAY_KILL_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoPlaySoundToSet(m_creature,SOUND_DIE); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( MortalWound_Timer < diff ) + { + if( HeroicMode ) DoCast(m_creature->getVictim(),H_SPELL_MORTAL_WOUND); + else DoCast(m_creature->getVictim(),SPELL_MORTAL_WOUND); + + MortalWound_Timer = 5000+rand()%8000; + }else MortalWound_Timer -= diff; + + if( Surge_Timer < diff ) + { + DoYell(SAY_SURGE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SURGE); + + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_SURGE); + + Surge_Timer = 5000+rand()%8000; + }else Surge_Timer -= diff; + + if((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 20) + { + if( Retaliation_Timer < diff ) + { + DoCast(m_creature,SPELL_RETALIATION); + Retaliation_Timer = 30000; + }else Retaliation_Timer -= diff; + } + + if( !YelledForHeal ) + if((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 40) + { + DoYell(SAY_HEAL, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_HEAL); + YelledForHeal = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_watchkeeper_gargolmarAI(Creature *_Creature) +{ + return new boss_watchkeeper_gargolmarAI (_Creature); +} + +void AddSC_boss_watchkeeper_gargolmar() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_watchkeeper_gargolmar"; + newscript->GetAI = GetAI_boss_watchkeeper_gargolmarAI; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp new file mode 100644 index 00000000000..32da6416b79 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp @@ -0,0 +1,437 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Magtheridon +SD%Complete: 60 +SDComment: In Development +SDCategory: Hellfire Citadel, Magtheridon's lair +EndScriptData */ + +#include "precompiled.h" +#include "def_magtheridons_lair.h" +#include "WorldPacket.h" + +//Phase 2 Spells +#define SPELL_QUAKE_PROC 30571 +#define SPELL_QUAKE 30576 //must be cast with 30561 as the proc spell +#define SPELL_BLASTNOVA 30616 +#define SPELL_CLEAVE 30619 +#define SPELL_BERSERK 27680 +#define SPELL_DEBRIS 30631 +#define SPELL_CAMERA_SHAKE 36455 + +//Banish +#define SPELL_SHADOW_CAGE 30205 + +//Player version of the banish +#define SPELL_SHADOW_CAGE_2 30168 + +//Spell that is cast on players from the cube +#define SPELL_SHADOW_GRASP 30410 +#define SPELL_SHADOW_GRASP_UKN 30166 +#define SPELL_SHADOW_GRASP_VIS 30207 + +//Dialog +#define SAY_AGGRO "Thank you for releasing me. Now...die!" +#define SOUND_AGGRO 10254 + +#define SAY_BANISH "Not again...NOT AGAIN!" +#define SOUND_BANISH 10256 + +#define SAY_FREED "I...am...UNLEASHED!!!" +#define SOUND_FREED 10253 + +#define SAY_CHAMBER_DESTROY "I will not be taken so easily. Let the walls of this prison tremble...and FALL!!!" +#define SOUND_CHAMBER_DESTROY 10257 + +#define SAY_PLAYER_KILLED "Did you think me weak? Soft? Who is the weak one now?!" +#define SOUND_PLAYER_KILLED 10255 + +#define SAY_DEATH "The Legion...will consume you...all...." +#define SOUND_DEATH 10258 + +#define EMOTE_BERSERK "becomes enraged!" +#define EMOTE_BLASTNOVA "begins to cast Blast Nova!" +#define EMOTE_BEGIN "%s's bonds begin to weaken!" + +//Spawned objects +#define SPELL_COLLAPSE 34233 //This spell casted by the "cave in" type object + +#define SPELL_CONFLAGERATION 35840 //Actually casted by a creature or object spawned on the ground + +//Cubes +#define SPELL_MIND_EXHAUSTIOIN 30509 //Casted by the cubes when channeling ends + +//Channeler spells +//#define MOB_HELLFIRE_CHANNELLER 17256 + +#define SPELL_SOUL_TRANSFER 30531 +#define SPELL_SHADOW_BOLT_VOLLEY 30510 +#define SPELL_DARK_MENDING 30528 +#define SPELL_HELLFIRE_CHANNELING 31059 +#define SPELL_HELLFIRE_CAST_VISUAL 24207 +#define SPELL_FEAR 39176 + +#define SPELL_BURNING_ABYSSAL 30511 + +// Unkown sounds +uint32 RandomSound[] = {10247, 10248, 10249, 10250, 10251, 10252}; + +struct MANGOS_DLL_DECL boss_magtheridonAI : public ScriptedAI +{ + boss_magtheridonAI(Creature *c) : ScriptedAI(c) + { + pInst = (ScriptedInstance*)m_creature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* pInst; + + uint32 Phase1_Timer; + uint32 Cleave_Timer; + uint32 BlastNova_Timer; + uint32 Quake_Timer; + uint32 QuakePhase; + uint32 Collapse_Timer; + uint32 Berserk_Timer; + bool Banished; + bool Phase3; + + uint32 RandChat_Timer; + + void Reset() + { + RandChat_Timer = 90000; + + Phase1_Timer = 0; + Cleave_Timer = 15000; + Berserk_Timer = 1200000; //20 minutes + BlastNova_Timer = 60000; + Quake_Timer = 40000; + QuakePhase = 0; + Collapse_Timer = 0; + Banished = false; + + m_creature->setFaction(35); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->CastSpell(m_creature, SPELL_SHADOW_CAGE, false); + + if(pInst) + pInst->SetData(DATA_MAGTHERIDON_EVENT_ENDED, false); + } + + void KilledUnit(Unit* victim) + { + DoYell(SAY_PLAYER_KILLED, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PLAYER_KILLED); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void Aggro(Unit *who) {} + + void MoveInLineOfSight(Unit* who) {} + + void UpdateAI(const uint32 diff) + { + if (!InCombat && !Phase1_Timer) + if (RandChat_Timer < diff) + { + DoPlaySoundToSet(m_creature, RandomSound[rand()%5]); + + RandChat_Timer = 90000; + }else RandChat_Timer -= diff; + + if (!InCombat && !Phase1_Timer && pInst && pInst->GetData64(DATA_EVENT_STARTER)) + { + //Unbanish self after 2 minutes + Phase1_Timer = 120000; + DoTextEmote(EMOTE_BEGIN, NULL); + return; + } + + //Phase timer + if (Phase1_Timer) + if (Phase1_Timer <= diff) + { + m_creature->setFaction(14); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + DoYell(SAY_FREED, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_FREED); + m_creature->RemoveAurasDueToSpell(SPELL_SHADOW_CAGE); + AttackStart(Unit::GetUnit(*m_creature, pInst->GetData64(DATA_EVENT_STARTER))); + + Phase1_Timer = 0; + }else + { + if (!Unit::GetUnit(*m_creature, pInst->GetData64(DATA_EVENT_STARTER))) + { + Phase1_Timer = 0; + return; + } + + Phase1_Timer -= diff; + return; + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Interrupt Blast Nova + if (m_creature->HasAura(SPELL_SHADOW_GRASP_VIS, 0) && m_creature->HasAura(SPELL_BLASTNOVA, 0) && !Banished) + { + DoYell(SAY_BANISH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_BANISH); + m_creature->RemoveAurasDueToSpell(SPELL_BLASTNOVA); + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature, SPELL_SHADOW_CAGE_2); + Banished = true; + } + + if (Banished && !m_creature->HasAura(SPELL_SHADOW_GRASP_VIS, 0)) + { + Banished = false; + m_creature->RemoveAurasDueToSpell(SPELL_SHADOW_CAGE_2); + } + + //Berserk_Timer + if (Berserk_Timer < diff) + { + DoCast(m_creature, SPELL_BERSERK); + DoTextEmote(EMOTE_BERSERK, NULL); + + Berserk_Timer = 300000; + }else Berserk_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 10000; + }else Cleave_Timer -= diff; + + //Quake_Timer + if (Quake_Timer < diff) + { + int32 i = SPELL_QUAKE_PROC; + m_creature->CastCustomSpell(m_creature, SPELL_QUAKE, &i, 0, 0, false); + + Quake_Timer = 40000; + }else Quake_Timer -= diff; + + //BlastNova_Timer + if (BlastNova_Timer < diff) + { + //Inturrupt Quake if it is casting + m_creature->InterruptNonMeleeSpells(false); + + DoTextEmote(EMOTE_BLASTNOVA, NULL); + DoCast(m_creature, SPELL_BLASTNOVA); + + BlastNova_Timer = 40000; + }else BlastNova_Timer -= diff; + + //Phase3 if not already enraged and below 30% + if (!Phase3 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 30) + { + Phase3 = true; + + DoYell(SAY_CHAMBER_DESTROY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CHAMBER_DESTROY); + } + + //Melee + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_hellfire_channelerAI : public ScriptedAI +{ + mob_hellfire_channelerAI(Creature *c) : ScriptedAI(c) + { + pInst = (ScriptedInstance*)m_creature->GetInstanceData(); + Reset(); + } + + ScriptedInstance* pInst; + + uint32 ShadowBoltVolley_Timer; + uint32 DarkMending_Timer; + uint32 Fear_Timer; + uint32 Infernal_Timer; + + bool InfernalSpawned; + + void Reset() + { + ShadowBoltVolley_Timer = 8000 + rand()%2000; + DarkMending_Timer = 30000; + Fear_Timer = 15000 + rand()%5000; + Infernal_Timer = 20000 + rand()%5000; + + InfernalSpawned = false; + + //Suprisingly this works very well, but only if the channelers are spawned after magtheridon + DoCast(m_creature, SPELL_SHADOW_GRASP_VIS); + if(pInst) + pInst->SetData(DATA_MAGTHERIDON_EVENT_ENDED, false); + } + + void Aggro(Unit *who) + { + m_creature->InterruptNonMeleeSpells(false); + + if(!pInst || pInst->GetData64(DATA_EVENT_STARTER)) + return; + + pInst->SetData64(DATA_EVENT_STARTER, who->GetGUID()); + pInst->SetData(DATA_MAGTHERIDON_EVENT_STARTED, true); + } + + void MoveInLineOfSight(Unit*) + { + } + + void UpdateAI(const uint32 diff) + { + if (!InCombat && pInst && pInst->GetData64(DATA_EVENT_STARTER)) + { + m_creature->InterruptNonMeleeSpells(false); + AttackStart(Unit::GetUnit(*m_creature, pInst->GetData64(DATA_EVENT_STARTER))); + return; + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Shadow bolt volley + if (ShadowBoltVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOW_BOLT_VOLLEY); + + ShadowBoltVolley_Timer = 10000 + (rand()%10000); + }else ShadowBoltVolley_Timer -= diff; + + //Dark Mending + if (DarkMending_Timer < diff) + { + if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 50) + { + //Cast on ourselves if we are lower then lowest hp friendly unit + /*if (pLowestHPTarget && LowestHP < m_creature->GetHealth()) + DoCast(pLowestHPTarget, SPELL_DARK_MENDING); + else*/ + DoCast(m_creature, SPELL_DARK_MENDING); + } + + DarkMending_Timer = 10000 + (rand() % 10000); + }else DarkMending_Timer -= diff; + + //Fear + if (Fear_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 1); + + if (target) + DoCast(target,SPELL_FEAR); + + Fear_Timer = 25000 + (rand()%15000); + }else Fear_Timer -= diff; + + //Infernal spawning + if (!InfernalSpawned && Infernal_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + if (target) + DoCast(target, SPELL_BURNING_ABYSSAL); + + InfernalSpawned = true; + }else Infernal_Timer -= diff; + + DoMeleeAttackIfReady(); + } + +}; + +//Manticron Cube +bool GOHello_go_Manticron_Cube(Player *player, GameObject* _GO) +{ + ScriptedInstance* pInst = (ScriptedInstance*)_GO->GetInstanceData(); + + Unit* pUnit = NULL; + if(pInst) + pUnit = Unit::GetUnit(*_GO, pInst->GetData64(DATA_MAGTHERIDON)); + else + { + _GO->TextEmote("Manticron Cube: NO INSTANCE", 0); + return true; + } + + if (!pUnit || !pUnit->isAlive() || !player) + { + _GO->TextEmote("Mantricon Cube: NO TARGET", 0); + return true; + } + + player->InterruptNonMeleeSpells(false); + player->CastSpell(pUnit, SPELL_SHADOW_GRASP, true); + player->CastSpell(pUnit, SPELL_SHADOW_GRASP_VIS, false); + + _GO->Say("Mantricon Cube Clicked", LANG_UNIVERSAL, 0); + return true; +} + +CreatureAI* GetAI_boss_magtheridon(Creature *_Creature) +{ + return new boss_magtheridonAI (_Creature); +} + +CreatureAI* GetAI_mob_hellfire_channeler(Creature *_Creature) +{ + return new mob_hellfire_channelerAI (_Creature); +} + +void AddSC_boss_magtheridon() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_magtheridon"; + newscript->GetAI = GetAI_boss_magtheridon; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_hellfire_channeler"; + newscript->GetAI = GetAI_mob_hellfire_channeler; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="go_manticron_cube"; + newscript->pGOHello = &GOHello_go_Manticron_Cube; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/def_magtheridons_lair.h b/src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/def_magtheridons_lair.h new file mode 100644 index 00000000000..dab98703c15 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/def_magtheridons_lair.h @@ -0,0 +1,13 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_MAGTHERIDONS_LAIR_H +#define DEF_MAGTHERIDONS_LAIR_H + +#define DATA_EVENT_STARTER 1 +#define DATA_MAGTHERIDON 2 +#define DATA_MAGTHERIDON_EVENT_ENDED 3 +#define DATA_MAGTHERIDON_EVENT_STARTED 4 +#define DATA_MAGTHERIDON_EVENT_STATUS 5 +#endif diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp new file mode 100644 index 00000000000..91ce5ce453c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp @@ -0,0 +1,116 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Magtheridons_Lair +SD%Complete: 100 +SDComment: +SDCategory: Hellfire Citadel, Magtheridon's lair +EndScriptData */ + +#include "precompiled.h" +#include "def_magtheridons_lair.h" + +struct MANGOS_DLL_DECL instance_magtheridons_lair : public ScriptedInstance +{ + instance_magtheridons_lair(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + bool EncounterInProgress; + uint64 Magtheridon; + uint64 EventStarter; + + void Initialize() + { + Magtheridon = 0; + EventStarter = 0; + EncounterInProgress = false; + } + + bool IsEncounterInProgress() const + { + return EncounterInProgress; + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + if (creature_entry == 17257) + Magtheridon = creature->GetGUID(); + } + + uint64 GetData64(uint32 identifier) + { + switch(identifier) + { + case DATA_MAGTHERIDON: + return Magtheridon; + + case DATA_EVENT_STARTER: + return EventStarter; + } + return 0; + } + + void SetData64(uint32 identifier, uint64 guid) + { + switch(identifier) + { + case DATA_MAGTHERIDON: + Magtheridon = guid; + break; + + case DATA_EVENT_STARTER: + EventStarter = guid; + break; + } + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_MAGTHERIDON_EVENT_STARTED: + EncounterInProgress = true; + break; + + case DATA_MAGTHERIDON_EVENT_ENDED: + EncounterInProgress = false; + EventStarter = 0; + break; + } + } + + uint32 GetData(uint32 type) + { + if(type == DATA_MAGTHERIDON_EVENT_STATUS) + return EncounterInProgress; + + return 0; + } +}; + +InstanceData* GetInstanceData_instance_magtheridons_lair(Map* map) +{ + return new instance_magtheridons_lair(map); +} + +void AddSC_instance_magtheridons_lair() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_magtheridons_lair"; + newscript->GetInstanceData = GetInstanceData_instance_magtheridons_lair; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_nethekurse.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_nethekurse.cpp new file mode 100644 index 00000000000..fa2619cc8fc --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_nethekurse.cpp @@ -0,0 +1,505 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Grand_Warlock_Nethekurse +SD%Complete: 75 +SDComment: encounter not fully complete. missing part where boss kill minions. +SDCategory: Hellfire Citadel, Shattered Halls +EndScriptData */ + +/* ContentData +boss_grand_warlock_nethekurse +mob_fel_orc_convert +mob_lesser_shadow_fissure +EndContentData */ + +#include "precompiled.h" +#include "def_shattered_halls.h" + +struct Say +{ + const char* text; + uint32 sound; +}; + +#define SAY_INTRO "You wish to fight us all at once? This should be amusing!" +#define SOUND_INTRO 10262 + +static Say PeonAttacked[]= +{ + {"You can have that one. I no longer need him.", 10263}, + {"Yes, beat him mercilessly. His skull is a thick as an ogres.", 10264}, + {"Don't waste your time on that one. He's weak!", 10265}, + {"You want him? Very well, take him!", 10266}, +}; +static Say PeonDies[]= +{ + {"One pitiful wretch down. Go on, take another one.", 10267}, + {"Ahh, what a waste... Next!", 10268}, + {"I was going to kill him anyway!", 10269}, + {"Thank you for saving me the trouble! Now it's my turn to have some fun...", 10270}, +}; + +#define SAY_TAUNT_1 "Beg for your pittyfull life!" +#define SOUND_TAUNT_1 10259 +#define SAY_TAUNT_2 "Run covad, ruun!" +#define SOUND_TAUNT_2 10260 +#define SAY_TAUNT_3 "Your pain amuses me." +#define SOUND_TAUNT_3 10261 + +#define SAY_AGGRO_1 "I'm already bored." +#define SOUND_AGGRO_1 10271 +#define SAY_AGGRO_2 "Come on! ... Show me a real fight." +#define SOUND_AGGRO_2 10272 +#define SAY_AGGRO_3 "I had more fun torturing the peons." +#define SOUND_AGGRO_3 10273 + +#define SAY_SLAY_1 "You Loose." +#define SOUND_SLAY_1 10274 +#define SAY_SLAY_2 "Ohh! Just die." +#define SOUND_SLAY_2 10275 + +#define SAY_DIE "What a ... a shame." +#define SOUND_DIE 10276 + +#define SPELL_DEATH_COIL 30500 +#define SPELL_DARK_SPIN 30502 // core bug spell attack caster :D +#define SPELL_SHADOW_FISSURE 30496 // Summon the ShadowFissure NPC + +#define SPELL_SHADOW_CLEAVE 30495 +#define H_SPELL_SHADOW_SLAM 35953 + +#define SPELL_HEMORRHAGE 30478 + +#define SPELL_CONSUMPTION 30497 +#define SPELL_TEMPORARY_VISUAL 39312 // this is wrong, a temporary solution. spell consumption already has the purple visual, but doesn't display as it should + +struct MANGOS_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI +{ + boss_grand_warlock_nethekurseAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + bool HeroicMode; + bool IntroOnce; + bool IsIntroEvent; + bool IsMainEvent; + bool SpinOnce; + //bool HasTaunted; + bool Phase; + + uint32 PeonEngagedCount; + uint32 PeonKilledCount; + + uint32 IntroEvent_Timer; + uint32 DeathCoil_Timer; + uint32 ShadowFissure_Timer; + uint32 Cleave_Timer; + + void Reset() + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + HeroicMode = m_creature->GetMap()->IsHeroic(); + IsIntroEvent = false; + IntroOnce = false; + IsMainEvent = false; + //HasTaunted = false; + SpinOnce = false; + Phase = false; + + PeonEngagedCount = 0; + PeonKilledCount = 0; + + IntroEvent_Timer = 90000; //how long before getting bored and kills his minions? + DeathCoil_Timer = 20000; + ShadowFissure_Timer = 8000; + Cleave_Timer = 5000; + } + + void DoYellForPeonAggro() + { + if( PeonEngagedCount >= 4 ) + return; + + DoYell(PeonAttacked[PeonEngagedCount].text, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, PeonAttacked[PeonEngagedCount].sound); + ++PeonEngagedCount; + } + + void DoYellForPeonDeath() + { + if( PeonKilledCount >= 4 ) + return; + + DoYell(PeonDies[PeonKilledCount].text, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, PeonDies[PeonKilledCount].sound); + ++PeonKilledCount; + + if( PeonKilledCount == 4 ) + { + IsIntroEvent = false; + IsMainEvent = true; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + } + + void DoTauntPeons() + { + switch(rand()%3) + { + case 0: + DoPlaySoundToSet(m_creature,SOUND_TAUNT_1); + DoYell(SAY_TAUNT_1,LANG_UNIVERSAL,NULL); + break; + case 1: + DoPlaySoundToSet(m_creature,SOUND_TAUNT_2); + DoYell(SAY_TAUNT_2,LANG_UNIVERSAL,NULL); + break; + case 2: + DoPlaySoundToSet(m_creature,SOUND_TAUNT_3); + DoYell(SAY_TAUNT_3,LANG_UNIVERSAL,NULL); + break; + } + + //TODO: kill the peons first + IsIntroEvent = false; + PeonEngagedCount = 4; + PeonKilledCount = 4; + IsMainEvent = true; + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + + void AttackStart(Unit* who) + { + if ( IsIntroEvent || !IsMainEvent ) + return; + + if( who->isTargetableForAttack() ) + { + if( Phase ) DoStartAttackNoMovement(who); + else DoStartAttackAndMovement(who); + + if( !InCombat ) + { + Aggro(who); + InCombat = true; + } + } + } + + void MoveInLineOfSight(Unit *who) + { + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if( !IntroOnce && m_creature->IsWithinDistInMap(who, 75) ) + { + DoYell(SAY_INTRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_INTRO); + IntroOnce = true; + IsIntroEvent = true; + + if( pInstance ) + pInstance->SetData(TYPE_NETHEKURSE,IN_PROGRESS); + } + + if( m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE ) + return; + + if( IsIntroEvent || !IsMainEvent ) + return; + + float attackRadius = m_creature->GetAttackDistance(who); + if( m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who) ) + { + if( Phase ) DoStartAttackNoMovement(who); + else DoStartAttackAndMovement(who); + + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + if( !InCombat ) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoPlaySoundToSet(m_creature,SOUND_AGGRO_1); + DoYell(SAY_AGGRO_1,LANG_UNIVERSAL,NULL); + break; + case 1: + DoPlaySoundToSet(m_creature,SOUND_AGGRO_2); + DoYell(SAY_AGGRO_2,LANG_UNIVERSAL,NULL); + break; + case 2: + DoPlaySoundToSet(m_creature,SOUND_AGGRO_3); + DoYell(SAY_AGGRO_3,LANG_UNIVERSAL,NULL); + break; + } + } + + void JustSummoned(Creature *summoned) + { + summoned->setFaction(14); + summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoPlaySoundToSet(m_creature,SOUND_SLAY_1); + DoYell(SAY_SLAY_1,LANG_UNIVERSAL,NULL); + break; + case 1: + DoPlaySoundToSet(m_creature,SOUND_SLAY_2); + DoYell(SAY_SLAY_2,LANG_UNIVERSAL,NULL); + break; + } + } + + void JustDied(Unit* Killer) + { + DoPlaySoundToSet(m_creature,SOUND_DIE); + DoYell(SAY_DIE,LANG_UNIVERSAL,NULL); + + if( !pInstance ) + return; + + pInstance->SetData(TYPE_NETHEKURSE,DONE); + + if( pInstance->GetData64(DATA_NETHEKURSE_DOOR) ) + { + if( GameObject *Door = GameObject::GetGameObject(*m_creature,pInstance->GetData64(DATA_NETHEKURSE_DOOR)) ) + Door->SetGoState(0); + } + } + + void UpdateAI(const uint32 diff) + { + if( IsIntroEvent ) + { + if( !pInstance ) + return; + + if( pInstance->GetData(TYPE_NETHEKURSE) == IN_PROGRESS ) + { + if( IntroEvent_Timer < diff ) + { + DoTauntPeons(); + }else IntroEvent_Timer -= diff; + } + } + + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( !IsMainEvent ) + return; + + if( Phase ) + { + if( !SpinOnce ) + { + DoCast(m_creature->getVictim(),SPELL_DARK_SPIN); + SpinOnce = true; + } + + if( Cleave_Timer < diff ) + { + DoCast(m_creature->getVictim(),(HeroicMode ? H_SPELL_SHADOW_SLAM : SPELL_SHADOW_CLEAVE)); + Cleave_Timer = 6000+rand()%2500; + }else Cleave_Timer -= diff; + } + else + { + if( ShadowFissure_Timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_SHADOW_FISSURE); + ShadowFissure_Timer = 7500+rand()%7500; + }else ShadowFissure_Timer -= diff; + + if( DeathCoil_Timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_DEATH_COIL); + DeathCoil_Timer = 15000+rand()%5000; + }else DeathCoil_Timer -= diff; + + if( (m_creature->GetHealth()*100) / m_creature->GetMaxHealth() <= 20 ) + Phase = true; + + DoMeleeAttackIfReady(); + } + } +}; + +struct MANGOS_DLL_DECL mob_fel_orc_convertAI : public ScriptedAI +{ + mob_fel_orc_convertAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + uint32 Hemorrhage_Timer; + + void Reset() + { + m_creature->SetNoCallAssistence(true); //we don't want any assistance (WE R HEROZ!) + Hemorrhage_Timer = 3000; + } + + void MoveInLineOfSight(Unit *who) + { + return; + } + + void Aggro(Unit* who) + { + if( pInstance ) + { + if( pInstance->GetData64(DATA_NETHEKURSE) ) + { + Creature *pKurse = (Creature*)Unit::GetUnit(*m_creature,pInstance->GetData64(DATA_NETHEKURSE)); + if( pKurse ) + ((boss_grand_warlock_nethekurseAI*)pKurse->AI())->DoYellForPeonAggro(); + } + + if( pInstance->GetData(TYPE_NETHEKURSE) == IN_PROGRESS ) + return; + else pInstance->SetData(TYPE_NETHEKURSE,IN_PROGRESS); + } + } + + void JustDied(Unit* Killer) + { + if( pInstance ) + { + if( pInstance->GetData64(DATA_NETHEKURSE) ) + { + Creature *pKurse = (Creature*)Unit::GetUnit(*m_creature,pInstance->GetData64(DATA_NETHEKURSE)); + if( pKurse ) + ((boss_grand_warlock_nethekurseAI*)pKurse->AI())->DoYellForPeonDeath(); + } + } + } + + void UpdateAI(const uint32 diff) + { + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( Hemorrhage_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_HEMORRHAGE); + Hemorrhage_Timer = 15000; + }else Hemorrhage_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//NOTE: this creature are also summoned by other spells, for different creatures +struct MANGOS_DLL_DECL mob_lesser_shadow_fissureAI : public ScriptedAI +{ + mob_lesser_shadow_fissureAI(Creature *c) : ScriptedAI(c) {Reset();} + + bool Start; + uint32 Stop_Timer; + + void Reset() + { + Start = false; + Stop_Timer = 30000; + } + + void Aggro(Unit* who) { } + + void MoveInLineOfSight(Unit *who) { return; } + + void AttackStart(Unit* who) { return; } + + void UpdateAI(const uint32 diff) + { + if( !Start ) + { + //triggered spell of consumption does not properly show it's SpellVisual, hack it a bit + m_creature->CastSpell(m_creature,SPELL_TEMPORARY_VISUAL,true); + m_creature->CastSpell(m_creature,SPELL_CONSUMPTION,false); + Start = true; + } + + if( Stop_Timer < diff) + { + m_creature->setDeathState(JUST_DIED); + m_creature->SetHealth(0); + m_creature->CombatStop(); + m_creature->DeleteThreatList(); + }else Stop_Timer -= diff; + } +}; + +CreatureAI* GetAI_boss_grand_warlock_nethekurse(Creature *_Creature) +{ + return new boss_grand_warlock_nethekurseAI (_Creature); +} + +CreatureAI* GetAI_mob_fel_orc_convert(Creature *_Creature) +{ + return new mob_fel_orc_convertAI (_Creature); +} + +CreatureAI* GetAI_mob_lesser_shadow_fissure(Creature *_Creature) +{ + return new mob_lesser_shadow_fissureAI (_Creature); +} + +void AddSC_boss_grand_warlock_nethekurse() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_grand_warlock_nethekurse"; + newscript->GetAI = GetAI_boss_grand_warlock_nethekurse; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_fel_orc_convert"; + newscript->GetAI = GetAI_mob_fel_orc_convert; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_lesser_shadow_fissure"; + newscript->GetAI = GetAI_mob_lesser_shadow_fissure; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp new file mode 100644 index 00000000000..96b4e6f29a0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp @@ -0,0 +1,414 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Warbringer_Omrogg +SD%Complete: 85 +SDComment: Heroic enabled. Spell timing may need additional tweaks +SDCategory: Hellfire Citadel, Shattered Halls +EndScriptData */ + +/* ContentData +mob_omrogg_heads +boss_warbringer_omrogg +EndContentData */ + +#include "precompiled.h" +#include "def_shattered_halls.h" + +#define ENTRY_LEFT_HEAD 19523 +#define ENTRY_RIGHT_HEAD 19524 + +struct Yell +{ + const char* text; + uint32 sound; + uint32 creature; +}; + +static Yell GoCombat[]= +{ + {"Smash!", 10306, ENTRY_LEFT_HEAD}, + {"If you nice me let you live.", 10308, ENTRY_LEFT_HEAD}, + {"Me hungry!", 10309, ENTRY_LEFT_HEAD}, +}; +static Yell GoCombatDelay[]= +{ + {"Why don't you let me do the talking?", 10317, ENTRY_RIGHT_HEAD}, + {"No, we will NOT let you live!", 10318, ENTRY_RIGHT_HEAD}, + {"You always hungry. That why we so fat!", 10319, ENTRY_RIGHT_HEAD}, +}; + +static Yell Threat[]= +{ + {"You stay here. Me go kill someone else!", 10303, ENTRY_LEFT_HEAD}, + {"What are you doing!", 10315, ENTRY_RIGHT_HEAD}, + {"Me kill someone else...", 10302, ENTRY_LEFT_HEAD}, + {"Me not like this one...",10300, ENTRY_LEFT_HEAD}, +}; +static Yell ThreatDelay1[]= +{ + {"That's not funny!", 10314, ENTRY_RIGHT_HEAD}, + {"Me get bored...", 10305, ENTRY_LEFT_HEAD}, + {"I'm not done yet, idiot!", 10313, ENTRY_RIGHT_HEAD}, + {"Hey you numbskull!", 10312, ENTRY_RIGHT_HEAD}, +}; +static Yell ThreatDelay2[]= +{ + {"Ha ha ha.", 10304, ENTRY_LEFT_HEAD}, + {"Whhy! He almost dead!", 10316, ENTRY_RIGHT_HEAD}, + {"H'ey...", 10307, ENTRY_LEFT_HEAD}, + {"We kill his friend!", 10301, ENTRY_LEFT_HEAD}, +}; + +static Yell Killing[]= +{ + {"This one die easy!", 10310, ENTRY_LEFT_HEAD}, + {"I'm tired. You kill next one!", 10320, ENTRY_RIGHT_HEAD}, +}; +static Yell KillingDelay[]= +{ + {"That's because I do all the hard work!", 10321, ENTRY_RIGHT_HEAD}, + {"SD2 script error, should not see this.", 0, ENTRY_LEFT_HEAD}, +}; + +#define EMOTE_ENRAGE "enrages" + +#define YELL_DIE_L "This all...your fault!" +#define SOUND_DIE_L 10311 +#define YELL_DIE_R "I...hate...you..." +#define SOUND_DIE_R 10322 + +#define SPELL_BLAST_WAVE 30600 +#define SPELL_FEAR 30584 +#define SPELL_THUNDERCLAP 30633 + +#define SPELL_BURNING_MAUL 30598 +#define H_SPELL_BURNING_MAUL 36056 + +struct MANGOS_DLL_DECL mob_omrogg_headsAI : public ScriptedAI +{ + mob_omrogg_headsAI(Creature *c) : ScriptedAI(c) { Reset(); } + + bool DeathYell; + uint32 Death_Timer; + + void Reset() {} + void Aggro(Unit* who) { } + + void DoDeathYell() + { + Death_Timer = 4000; + DeathYell = true; + } + + void UpdateAI(const uint32 diff) + { + if( !DeathYell ) + return; + + if( Death_Timer < diff ) + { + DoYell(YELL_DIE_R,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DIE_R); + DeathYell = false; + }else Death_Timer -= diff; + } +}; + +struct MANGOS_DLL_DECL boss_warbringer_omroggAI : public ScriptedAI +{ + boss_warbringer_omroggAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + HeroicMode = m_creature->GetMap()->IsHeroic(); + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 LeftHead; + uint64 RightHead; + int iaggro; + int ithreat; + int ikilling; + + bool HeroicMode; + bool AggroYell; + bool ThreatYell; + bool ThreatYell2; + bool KillingYell; + + uint32 Delay_Timer; + uint32 BlastWave_Timer; + uint32 BlastCount; + uint32 Fear_Timer; + uint32 BurningMaul_Timer; + uint32 ThunderClap_Timer; + uint32 ResetThreat_Timer; + + void Reset() + { + m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); + m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true); + + LeftHead = 0; + RightHead = 0; + + AggroYell = false; + ThreatYell = false; + ThreatYell2 = false; + KillingYell = false; + + Delay_Timer = 4000; + BlastWave_Timer = 0; + BlastCount = 0; + Fear_Timer = 8000; + BurningMaul_Timer = 25000; + ThunderClap_Timer = 15000; + ResetThreat_Timer = 30000; + + if( pInstance ) + pInstance->SetData(TYPE_OMROGG, NOT_STARTED); //End boss can use this later. O'mrogg must be defeated(DONE) or he will come to aid. + } + + void DoYellForThreat() + { + if( LeftHead && RightHead ) + { + Unit *Left = Unit::GetUnit(*m_creature,LeftHead); + Unit *Right = Unit::GetUnit(*m_creature,RightHead); + + if( !Left && !Right ) + return; + + ithreat = rand()%4; + + Unit *source = (Left->GetEntry() == Threat[ithreat].creature ? Left : Right); + + source->MonsterYell(Threat[ithreat].text, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(source, Threat[ithreat].sound); + + Delay_Timer = 3500; + ThreatYell = true; + } + } + + void Aggro(Unit *who) + { + DoSpawnCreature(ENTRY_LEFT_HEAD,0,0,0,0,TEMPSUMMON_TIMED_DESPAWN,1800000); + DoSpawnCreature(ENTRY_RIGHT_HEAD,0,0,0,0,TEMPSUMMON_TIMED_DESPAWN,1800000); + + if( Unit *Left = Unit::GetUnit(*m_creature,LeftHead) ) + { + iaggro = rand()%3; + + Left->MonsterYell(GoCombat[iaggro].text, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(Left, GoCombat[iaggro].sound); + + Delay_Timer = 3500; + AggroYell = true; + } + if( pInstance ) + pInstance->SetData(TYPE_OMROGG, IN_PROGRESS); + } + + void JustSummoned(Creature *summoned) + { + if( summoned->GetEntry() == ENTRY_LEFT_HEAD ) + LeftHead = summoned->GetGUID(); + + if( summoned->GetEntry() == ENTRY_RIGHT_HEAD ) + RightHead = summoned->GetGUID(); + + //summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + //summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + summoned->SetVisibility(VISIBILITY_OFF); + } + + void KilledUnit(Unit* victim) + { + if( LeftHead && RightHead ) + { + Unit *Left = Unit::GetUnit(*m_creature,LeftHead); + Unit *Right = Unit::GetUnit(*m_creature,RightHead); + + if( !Left && !Right ) + return; + + ikilling = rand()%2; + + Unit *source = (Left->GetEntry() == Killing[ikilling].creature ? Left : Right); + + switch(ikilling) + { + case 0: + source->MonsterYell(Killing[ikilling].text, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(source, Killing[ikilling].sound); + Delay_Timer = 3500; + KillingYell = true; + break; + case 1: + source->MonsterYell(Killing[ikilling].text, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(source, Killing[ikilling].sound); + KillingYell = false; + break; + } + } + } + + void JustDied(Unit* Killer) + { + if( LeftHead && RightHead ) + { + Unit *Left = Unit::GetUnit(*m_creature,LeftHead); + Unit *Right = Unit::GetUnit(*m_creature,RightHead); + + if( !Left && !Right ) + return; + + Left->MonsterYell(YELL_DIE_L, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(Left,SOUND_DIE_L); + + ((mob_omrogg_headsAI*)((Creature*)Right)->AI())->DoDeathYell(); + } + if( pInstance ) + pInstance->SetData(TYPE_OMROGG, DONE); + } + + void UpdateAI(const uint32 diff) + { + if( Delay_Timer < diff ) + { + Delay_Timer = 3500; + + if( !LeftHead && !RightHead ) + return; + + Unit *Left = Unit::GetUnit(*m_creature,LeftHead); + Unit *Right = Unit::GetUnit(*m_creature,RightHead); + + if( !Left && !Right ) + return; + + if( AggroYell ) + { + Right->MonsterYell(GoCombatDelay[iaggro].text, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(Right, GoCombatDelay[iaggro].sound); + AggroYell = false; + } + + if( ThreatYell2 ) + { + Unit *source = (Left->GetEntry() == ThreatDelay2[ithreat].creature ? Left : Right); + + source->MonsterYell(ThreatDelay2[ithreat].text, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(source, ThreatDelay2[ithreat].sound); + ThreatYell2 = false; + } + + if( ThreatYell ) + { + Unit *source = (Left->GetEntry() == ThreatDelay1[ithreat].creature ? Left : Right); + + source->MonsterYell(ThreatDelay1[ithreat].text, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(source, ThreatDelay1[ithreat].sound); + ThreatYell = false; + ThreatYell2 = true; + } + + if( KillingYell ) + { + Unit *source = (Left->GetEntry() == KillingDelay[ikilling].creature ? Left : Right); + + source->MonsterYell(KillingDelay[ikilling].text, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(source, KillingDelay[ikilling].sound); + KillingYell = false; + } + }else Delay_Timer -= diff; + + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( BlastCount && BlastWave_Timer <= diff ) + { + DoCast(m_creature,SPELL_BLAST_WAVE); + BlastWave_Timer = 5000; + ++BlastCount; + if( BlastCount == 3 ) + BlastCount = 0; + }else BlastWave_Timer -= diff; + + if( BurningMaul_Timer < diff ) + { + DoTextEmote(EMOTE_ENRAGE,NULL); + DoCast(m_creature,HeroicMode ? H_SPELL_BURNING_MAUL : SPELL_BURNING_MAUL); + BurningMaul_Timer = 40000; + BlastWave_Timer = 16000; + BlastCount = 1; + }else BurningMaul_Timer -= diff; + + if( ResetThreat_Timer < diff ) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + { + DoYellForThreat(); + DoResetThreat(); + m_creature->AddThreat(target, 0.0f); + } + ResetThreat_Timer = 35000+rand()%10000; + }else ResetThreat_Timer -= diff; + + if( Fear_Timer < diff ) + { + DoCast(m_creature,SPELL_FEAR); + Fear_Timer = 15000+rand()%25000; + }else Fear_Timer -= diff; + + if( ThunderClap_Timer < diff ) + { + DoCast(m_creature,SPELL_THUNDERCLAP); + ThunderClap_Timer = 25000+rand()%15000; + }else ThunderClap_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_warbringer_omrogg(Creature *_Creature) +{ + return new boss_warbringer_omroggAI (_Creature); +} + +CreatureAI* GetAI_mob_omrogg_heads(Creature *_Creature) +{ + return new mob_omrogg_headsAI (_Creature); +} + +void AddSC_boss_warbringer_omrogg() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_warbringer_omrogg"; + newscript->GetAI = GetAI_boss_warbringer_omrogg; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_omrogg_heads"; + newscript->GetAI = GetAI_mob_omrogg_heads; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/def_shattered_halls.h b/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/def_shattered_halls.h new file mode 100644 index 00000000000..602564d32f0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/def_shattered_halls.h @@ -0,0 +1,13 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SHATTERED_H +#define DEF_SHATTERED_H + +#define TYPE_NETHEKURSE 1 +#define DATA_NETHEKURSE 2 +#define DATA_NETHEKURSE_DOOR 3 + +#define TYPE_OMROGG 4 +#endif diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp new file mode 100644 index 00000000000..8d068a4bb40 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp @@ -0,0 +1,114 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Shattered_Halls +SD%Complete: 50 +SDComment: currently missing info about door. instance not complete +SDCategory: Hellfire Citadel, Shattered Halls +EndScriptData */ + +#include "precompiled.h" +#include "def_shattered_halls.h" + +#define ENCOUNTERS 2 + +#define DOOR_NETHEKURSE 1 + +struct MANGOS_DLL_DECL instance_shattered_halls : public ScriptedInstance +{ + instance_shattered_halls(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint32 Encounter[ENCOUNTERS]; + uint64 nethekurseGUID; + uint64 nethekurseDoorGUID; + + void Initialize() + { + nethekurseGUID = 0; + nethekurseDoorGUID = 0; + + for(uint8 i = 0; i < ENCOUNTERS; i++) + Encounter[i] = NOT_STARTED; + } + + void OnObjectCreate(GameObject *go) + { + switch( go->GetEntry() ) + { + case DOOR_NETHEKURSE: nethekurseDoorGUID = go->GetGUID(); break; + } + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch( creature_entry ) + { + case 16807: nethekurseGUID = creature->GetGUID(); break; + } + } + + void SetData(uint32 type, uint32 data) + { + switch( type ) + { + case TYPE_NETHEKURSE: + Encounter[0] = data; + break; + case TYPE_OMROGG: + Encounter[1] = data; + break; + } + } + + uint32 GetData(uint32 type) + { + switch( type ) + { + case TYPE_NETHEKURSE: + return Encounter[0]; + case TYPE_OMROGG: + return Encounter[1]; + } + return 0; + } + + uint64 GetData64(uint32 data) + { + switch(data) + { + case DATA_NETHEKURSE: + return nethekurseGUID; + case DATA_NETHEKURSE_DOOR: + return nethekurseDoorGUID; + } + return 0; + } +}; + +InstanceData* GetInstanceData_instance_shattered_halls(Map* map) +{ + return new instance_shattered_halls(map); +} + +void AddSC_instance_shattered_halls() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_shattered_halls"; + newscript->GetInstanceData = GetInstanceData_instance_shattered_halls; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_peninsula/boss_doomlord_kazzak.cpp b/src/bindings/scripts/scripts/zone/hellfire_peninsula/boss_doomlord_kazzak.cpp new file mode 100644 index 00000000000..ccb36f9f350 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_peninsula/boss_doomlord_kazzak.cpp @@ -0,0 +1,141 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Doomlord_Kazzak +SD%Complete: 70 +SDComment: Using incorrect spell for Mark of Kazzak +SDCategory: Hellfire Peninsula +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWVOLLEY 32963 +#define SPELL_CLEAVE 31779 +#define SPELL_THUNDERCLAP 36706 +#define SPELL_VOIDBOLT 39329 +#define SPELL_MARKOFKAZZAK 32960 +#define SPELL_ENRAGE 32964 +#define SPELL_CAPTURESOUL 32966 +#define SPELL_TWISTEDREFLECTION 21063 + +struct MANGOS_DLL_DECL boss_doomlordkazzakAI : public ScriptedAI +{ + boss_doomlordkazzakAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowVolley_Timer; + uint32 Cleave_Timer; + uint32 ThunderClap_Timer; + uint32 VoidBolt_Timer; + uint32 MarkOfKazzak_Timer; + uint32 Enrage_Timer; + uint32 Twisted_Reflection_Timer; + + void Reset() + { + ShadowVolley_Timer = 8000 + rand()%4000; + Cleave_Timer = 7000; + ThunderClap_Timer = 16000 + rand()%4000; + VoidBolt_Timer = 30000; + MarkOfKazzak_Timer = 25000; + Enrage_Timer = 60000; + Twisted_Reflection_Timer = 33000; // Timer may be incorrect + } + + void Aggro(Unit *who) {} + + void KilledUnit(Unit* victim) + { + // When Kazzak kills a player (not pets/totems), he regens some health + if(victim->GetTypeId() == TYPEID_PLAYER) + DoCast(m_creature,SPELL_CAPTURESOUL); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ShadowVolley_Timer + if (ShadowVolley_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SHADOWVOLLEY); + ShadowVolley_Timer = 4000 + rand()%2000; + }else ShadowVolley_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 8000 + rand()%4000; + }else Cleave_Timer -= diff; + + //ThunderClap_Timer + if (ThunderClap_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_THUNDERCLAP); + ThunderClap_Timer = 10000 + rand()%4000; + }else ThunderClap_Timer -= diff; + + //VoidBolt_Timer + if (VoidBolt_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_VOIDBOLT); + VoidBolt_Timer = 15000 + rand()%3000; + }else VoidBolt_Timer -= diff; + + //MarkOfKazzak_Timer + if (MarkOfKazzak_Timer < diff) + { + Unit* victim = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(victim->GetPower(POWER_MANA)) + { + DoCast(victim, SPELL_MARKOFKAZZAK); + MarkOfKazzak_Timer = 20000; + } + }else MarkOfKazzak_Timer -= diff; + + //Enrage_Timer + if (Enrage_Timer < diff) + { + DoCast(m_creature,SPELL_ENRAGE); + Enrage_Timer = 30000; + }else Enrage_Timer -= diff; + + if(Twisted_Reflection_Timer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_TWISTEDREFLECTION); + Twisted_Reflection_Timer = 15000; + }else Twisted_Reflection_Timer -= diff; + + DoMeleeAttackIfReady(); + } + +}; +CreatureAI* GetAI_boss_doomlordkazzak(Creature *_Creature) +{ + return new boss_doomlordkazzakAI (_Creature); +} + +void AddSC_boss_doomlordkazzak() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_doomlord_kazzak"; + newscript->GetAI = GetAI_boss_doomlordkazzak; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/hellfire_peninsula/hellfire_peninsula.cpp b/src/bindings/scripts/scripts/zone/hellfire_peninsula/hellfire_peninsula.cpp new file mode 100644 index 00000000000..ceedce42ea2 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/hellfire_peninsula/hellfire_peninsula.cpp @@ -0,0 +1,187 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Hellfire_Peninsula +SD%Complete: 100 +SDComment: Quest support: 10129, 10146, 10162, 10163, 10340, 10346, 10347, 10382 (Special flight paths) +SDCategory: Hellfire Peninsula +EndScriptData */ + +/* ContentData +npc_wing_commander_dabiree +npc_gryphoneer_windbellow +npc_wing_commander_brack +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_wing_commander_dabiree +######*/ + +#define GOSSIP_ITEM1_DAB "Fly me to Murketh and Shaadraz Gateways" +#define GOSSIP_ITEM2_DAB "Fly me to Shatter Point" + +bool GossipHello_npc_wing_commander_dabiree(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + //Mission: The Murketh and Shaadraz Gateways + if (player->GetQuestStatus(10146) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM(2, GOSSIP_ITEM1_DAB, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + //Shatter Point + if (!player->GetQuestRewardStatus(10340)) + player->ADD_GOSSIP_ITEM(2, GOSSIP_ITEM2_DAB, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_wing_commander_dabiree(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,33768,true); //TaxiPath 585 (Gateways Murket and Shaadraz) + } + if (action == GOSSIP_ACTION_INFO_DEF + 2) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,35069,true); //TaxiPath 612 (Taxi - Hellfire Peninsula - Expedition Point to Shatter Point) + } + return true; +} + +/*###### +## npc_gryphoneer_windbellow +######*/ + +#define GOSSIP_ITEM1_WIN "Fly me to The Abyssal Shelf" +#define GOSSIP_ITEM2_WIN "Fly me to Honor Point" + +bool GossipHello_npc_gryphoneer_windbellow(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + //Mission: The Abyssal Shelf || Return to the Abyssal Shelf + if (player->GetQuestStatus(10163) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(10346) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM(2, GOSSIP_ITEM1_WIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + //Go to the Front + if (player->GetQuestStatus(10382) != QUEST_STATUS_NONE && !player->GetQuestRewardStatus(10382)) + player->ADD_GOSSIP_ITEM(2, GOSSIP_ITEM2_WIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_gryphoneer_windbellow(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,33899,true); //TaxiPath 589 (Aerial Assault Flight (Alliance)) + } + if (action == GOSSIP_ACTION_INFO_DEF + 2) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,35065,true); //TaxiPath 607 (Taxi - Hellfire Peninsula - Shatter Point to Beach Head) + } + return true; +} + +/*###### +## npc_wing_commander_brack +######*/ + +#define GOSSIP_ITEM1_BRA "Fly me to Murketh and Shaadraz Gateways" +#define GOSSIP_ITEM2_BRA "Fly me to The Abyssal Shelf" +#define GOSSIP_ITEM3_BRA "Fly me to Spinebreaker Post" + +bool GossipHello_npc_wing_commander_brack(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + //Mission: The Murketh and Shaadraz Gateways + if (player->GetQuestStatus(10129) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM(2, GOSSIP_ITEM1_BRA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + //Mission: The Abyssal Shelf || Return to the Abyssal Shelf + if (player->GetQuestStatus(10162) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(10347) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM(2, GOSSIP_ITEM2_BRA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + + //Spinebreaker Post + if (player->GetQuestStatus(10242) == QUEST_STATUS_COMPLETE && !player->GetQuestRewardStatus(10242)) + player->ADD_GOSSIP_ITEM(2, GOSSIP_ITEM3_BRA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_wing_commander_brack(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,33659,true); //TaxiPath 584 (Gateways Murket and Shaadraz) + } + if (action == GOSSIP_ACTION_INFO_DEF + 2) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,33825,true); //TaxiPath 587 (Aerial Assault Flight (Horde)) + } + if (action == GOSSIP_ACTION_INFO_DEF + 3) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,34578,true); //TaxiPath 604 (Taxi - Reaver's Fall to Spinebreaker Ridge) + } + return true; +} + +/*###### +## +######*/ + +void AddSC_hellfire_peninsula() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_wing_commander_dabiree"; + newscript->pGossipHello = &GossipHello_npc_wing_commander_dabiree; + newscript->pGossipSelect = &GossipSelect_npc_wing_commander_dabiree; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_gryphoneer_windbellow"; + newscript->pGossipHello = &GossipHello_npc_gryphoneer_windbellow; + newscript->pGossipSelect = &GossipSelect_npc_gryphoneer_windbellow; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_wing_commander_brack"; + newscript->pGossipHello = &GossipHello_npc_wing_commander_brack; + newscript->pGossipSelect = &GossipSelect_npc_wing_commander_brack; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/ironforge/ironforge.cpp b/src/bindings/scripts/scripts/zone/ironforge/ironforge.cpp new file mode 100644 index 00000000000..5ba9e4b3ae1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/ironforge/ironforge.cpp @@ -0,0 +1,93 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Ironforge +SD%Complete: 100 +SDComment: Quest support: 3702 +SDCategory: Ironforge +EndScriptData */ + +/* ContentData +npc_royal_historian_archesonus +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_royal_historian_archesonus +######*/ + +#define GOSSIP_ITEM_ROYAL "I am ready to listen" +#define GOSSIP_ITEM_ROYAL_1 "That is tragic. How did this happen?" +#define GOSSIP_ITEM_ROYAL_2 "Interesting, continue please." +#define GOSSIP_ITEM_ROYAL_3 "Unbelievable! How dare they??" +#define GOSSIP_ITEM_ROYAL_4 "Of course I will help!" + +bool GossipHello_npc_royal_historian_archesonus(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(3702) == QUEST_STATUS_INCOMPLETE) + { + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_ROYAL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(2235, _Creature->GetGUID()); + } + else + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_royal_historian_archesonus(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_ROYAL_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(2236, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_ROYAL_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(2237, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_ROYAL_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(2238, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_ROYAL_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->SEND_GOSSIP_MENU(2239, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(3702); + break; + } + return true; +} + +void AddSC_ironforge() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_royal_historian_archesonus"; + newscript->pGossipHello = &GossipHello_npc_royal_historian_archesonus; + newscript->pGossipSelect = &GossipSelect_npc_royal_historian_archesonus; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/isle_of_queldanas/isle_of_queldanas.cpp b/src/bindings/scripts/scripts/zone/isle_of_queldanas/isle_of_queldanas.cpp new file mode 100644 index 00000000000..06baf994a18 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/isle_of_queldanas/isle_of_queldanas.cpp @@ -0,0 +1,155 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Isle_of_Queldanas +SD%Complete: 100 +SDComment: Quest support: 11524, 11525, 11532, 11533, 11542, 11543 +SDCategory: Isle Of Quel'Danas +EndScriptData */ + +/* ContentData +npc_ayren_cloudbreaker +npc_converted_sentry +npc_unrestrained_dragonhawk +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_ayren_cloudbreaker +######*/ + +bool GossipHello_npc_ayren_cloudbreaker(Player *player, Creature *_Creature) +{ + if( player->GetQuestStatus(11532) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(11533) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM(0,"Speaking of action, I've been ordered to undertake an air strike.",GOSSIP_SENDER_MAIN,GOSSIP_ACTION_INFO_DEF+1); + + if( player->GetQuestStatus(11542) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(11543) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM(0,"I need to intercept the Dawnblade reinforcements.",GOSSIP_SENDER_MAIN,GOSSIP_ACTION_INFO_DEF+2); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(),_Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_ayren_cloudbreaker(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,45071,true); //TaxiPath 779 + } + if (action == GOSSIP_ACTION_INFO_DEF+2) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,45113,true); //TaxiPath 784 + } + return true; +} + +/*###### +## npc_converted_sentry +######*/ + +#define SAY_CONVERTED_1 "Deployment sucessful. Trespassers will be neutralized." +#define SAY_CONVERTED_2 "Objective acquired. Initiating security routines." + +#define SPELL_CONVERT_CREDIT 45009 + +struct MANGOS_DLL_DECL npc_converted_sentryAI : public ScriptedAI +{ + npc_converted_sentryAI(Creature *c) : ScriptedAI(c) { Reset(); } + + bool Credit; + uint32 Timer; + + void Reset() + { + Credit = false; + Timer = 2500; + } + + void MoveInLineOfSight(Unit *who) + { return; } + void Aggro(Unit* who) + { } + + void UpdateAI(const uint32 diff) + { + if( !Credit ) + { + if( Timer <= diff ) + { + uint32 i = urand(1,2); + if( i=1 ) DoSay(SAY_CONVERTED_1,LANG_UNIVERSAL,NULL); + else DoSay(SAY_CONVERTED_2,LANG_UNIVERSAL,NULL); + + DoCast(m_creature,SPELL_CONVERT_CREDIT); + ((Pet*)m_creature)->SetDuration(7500); + Credit = true; + }else Timer -= diff; + } + } +}; +CreatureAI* GetAI_npc_converted_sentry(Creature *_Creature) +{ + return new npc_converted_sentryAI (_Creature); +} + +/*###### +## npc_unrestrained_dragonhawk +######*/ + +bool GossipHello_npc_unrestrained_dragonhawk(Player *player, Creature *_Creature) +{ + if( player->GetQuestStatus(11542) == QUEST_STATUS_COMPLETE || player->GetQuestStatus(11543) == QUEST_STATUS_COMPLETE ) + player->ADD_GOSSIP_ITEM(0,"",GOSSIP_SENDER_MAIN,GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(),_Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_unrestrained_dragonhawk(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,45353,true); //TaxiPath 788 + } + return true; +} + +void AddSC_isle_of_queldanas() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_ayren_cloudbreaker"; + newscript->pGossipHello = &GossipHello_npc_ayren_cloudbreaker; + newscript->pGossipSelect = &GossipSelect_npc_ayren_cloudbreaker; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_converted_sentry"; + newscript->GetAI = GetAI_npc_converted_sentry; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_unrestrained_dragonhawk"; + newscript->pGossipHello = &GossipHello_npc_unrestrained_dragonhawk; + newscript->pGossipSelect = &GossipSelect_npc_unrestrained_dragonhawk; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_curator.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_curator.cpp new file mode 100644 index 00000000000..b7c4af8dded --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_curator.cpp @@ -0,0 +1,200 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Curator +SD%Complete: 100 +SDComment: Evocation may cause client crash (Core related) +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" + +#define SAY_AGGRO "The Menagerie is for guests only." +#define SOUND_AGGRO 9183 + +#define SAY_SUMMON1 "Gallery rules will be strictly enforced." +#define SOUND_SUMMON1 9188 + +#define SAY_SUMMON2 "This curator is equipped for gallery protection." +#define SOUND_SUMMON2 9309 + +#define SAY_EVOCATE "Your request cannot be processed." +#define SOUND_EVOCATE 9186 + +#define SAY_ENRAGE "Failure to comply will result in offensive action." +#define SOUND_ENRAGE 9185 + +#define SAY_KILL1 "Do not touch the displays." +#define SOUND_KILL1 9187 + +#define SAY_KILL2 "You are not a guest." +#define SOUND_KILL2 9308 + +#define SAY_DEATH "This Curator is no longer op... er... ation... al." +#define SOUND_DEATH 9184 + +//Flare spell info +#define SPELL_ASTRAL_FLARE_PASSIVE 30234 +#define SPELL_ASTRAL_FLARE_NE 30236 +#define SPELL_ASTRAL_FLARE_NW 30239 +#define SPELL_ASTRAL_FLARE_SE 30240 +#define SPELL_ASTRAL_FLARE_SW 30241 + +//Curator spell info +#define SPELL_HATEFUL_BOLT 30383 +#define SPELL_EVOCATION 30254 +#define SPELL_ENRAGE 28131 +#define SPELL_BERSERK 26662 + +struct MANGOS_DLL_DECL boss_curatorAI : public ScriptedAI +{ + boss_curatorAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 AddTimer; + uint32 HatefulBoltTimer; + uint32 BerserkTimer; + + bool Enraged; + bool Evocating; + + void Reset() + { + AddTimer = 10000; + HatefulBoltTimer = 15000; // This time is probably wrong + BerserkTimer = 720000; //12 minutes + Enraged = false; + Evocating = false; + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(victim, SOUND_KILL1); + break; + case 1: + DoYell(SAY_KILL2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(victim, SOUND_KILL2); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(NULL, SOUND_DEATH); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if (Evocating && !m_creature->HasAura(SPELL_EVOCATION, 0)) + Evocating = false; + + if(m_creature->GetPower(POWER_MANA) <= 1000 && !Evocating) + { + DoYell(SAY_EVOCATE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_EVOCATE); + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature, SPELL_EVOCATION); + Evocating = true; + } + + if(!Enraged && !Evocating) + { + if(AddTimer < diff) + { + //Summon Astral Flare + Creature* AstralFlare = DoSpawnCreature(17096, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + if (AstralFlare && target) + { + AstralFlare->CastSpell(AstralFlare, SPELL_ASTRAL_FLARE_PASSIVE, false); + AstralFlare->AI()->AttackStart(target); + } + + //Reduce Mana by 10% + int32 mana = (int32)(0.1f*(m_creature->GetMaxPower(POWER_MANA))); + m_creature->ModifyPower(POWER_MANA, -mana); + switch(rand()%4) + { + case 0: + DoYell(SAY_SUMMON1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SUMMON1); + break; + case 1: + DoYell(SAY_SUMMON2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SUMMON2); + break; + } + AddTimer = 10000; + }else AddTimer -= diff; + + if(HatefulBoltTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_TOPAGGRO, 1); + DoCast(target, SPELL_HATEFUL_BOLT); + + HatefulBoltTimer = 15000; + }else HatefulBoltTimer -= diff; + + if(m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 15) + { + Enraged = true; + DoCast(m_creature, SPELL_ENRAGE); + DoYell(SAY_ENRAGE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + } + } + + if(BerserkTimer < diff) + { + DoCast(m_creature, SPELL_BERSERK); + DoYell(SAY_ENRAGE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ENRAGE); + }else BerserkTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_curator(Creature *_Creature) +{ + return new boss_curatorAI (_Creature); +} + +void AddSC_boss_curator() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_curator"; + newscript->GetAI = GetAI_boss_curator; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_maiden_of_virtue.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_maiden_of_virtue.cpp new file mode 100644 index 00000000000..45ee19c7999 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_maiden_of_virtue.cpp @@ -0,0 +1,176 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Maiden_of_Virtue +SD%Complete: 100 +SDComment: +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_REPENTANCE 29511 +#define SPELL_HOLYFIRE 29522 +#define SPELL_HOLYWRATH 32445 +#define SPELL_HOLYGROUND 29512 + +#define SAY_AGGRO "Your behavior will not be tolerated!" +#define SAY_SLAY1 "Ah ah ah..." +#define SAY_SLAY2 "This is for the best." +#define SAY_SLAY3 "Impure thoughts lead to profane actions." +#define SAY_REPENTANCE1 "Cast out your corrupt thoughts." +#define SAY_REPENTANCE2 "Your impurity must be cleansed." +#define SAY_DEATH "Death comes. Will your conscience be clear?" + +#define SOUND_AGGRO 9204 +#define SOUND_SLAY1 9207 +#define SOUND_SLAY2 9312 +#define SOUND_SLAY3 9311 +#define SOUND_REPENTANCE1 9313 +#define SOUND_REPENTANCE2 9208 +#define SOUND_DEATH 9206 + +struct MANGOS_DLL_DECL boss_maiden_of_virtueAI : public ScriptedAI +{ + boss_maiden_of_virtueAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Repentance_Timer; + uint32 Holyfire_Timer; + uint32 Holywrath_Timer; + uint32 Holyground_Timer; + + void Reset() + { + Repentance_Timer = 30000+(rand()%15000); + Holyfire_Timer = 8000+(rand()%17000); + Holywrath_Timer = 20000+(rand()%10000); + Holyground_Timer = 3000; + } + + void KilledUnit(Unit* Victim) + { + if(rand()%2) return; + + switch(rand()%3) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,Victim); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,Victim); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + case 2: + DoYell(SAY_SLAY3,LANG_UNIVERSAL,Victim); + DoPlaySoundToSet(m_creature, SOUND_SLAY3); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if (Holyground_Timer < diff) + { + DoCast(m_creature, SPELL_HOLYGROUND, true); //Triggered so it doesn't interrupt her at all + Holyground_Timer = 3000; + }else Holyground_Timer -= diff; + + if (Repentance_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_REPENTANCE); + + switch(rand()%2) + { + case 0: + DoYell(SAY_REPENTANCE1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_REPENTANCE1); + break; + case 1: + DoYell(SAY_REPENTANCE2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_REPENTANCE2); + break; + } + Repentance_Timer = 30000 + rand()%15000; //A little randomness on that spell + }else Repentance_Timer -= diff; + + if (Holyfire_Timer < diff) + { + //Time for an omgwtfpwn code to make maiden cast holy fire only on units outside the holy ground's 18 yard range + Unit* target = NULL; + std::list t_list = m_creature->getThreatManager().getThreatList(); + std::vector target_list; + for(std::list::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr) + { + target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + if(target && target->GetDistance2d(m_creature) > 12 ) + target_list.push_back(target); + target = NULL; + } + if(target_list.size()) + target = *(target_list.begin()+rand()%target_list.size()); + + DoCast(target,SPELL_HOLYFIRE); + + Holyfire_Timer = 8000 + rand()%17000; //Anywhere from 8 to 25 seconds, good luck having several of those in a row! + }else Holyfire_Timer -= diff; + + if (Holywrath_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + if (target) + { + DoCast(target,SPELL_HOLYWRATH); + Holywrath_Timer = 20000+(rand()%10000); //20-30 secs sounds nice + } + }else Holywrath_Timer -= diff; + + DoMeleeAttackIfReady(); + } + +}; + +CreatureAI* GetAI_boss_maiden_of_virtue(Creature *_Creature) +{ + return new boss_maiden_of_virtueAI (_Creature); +} + +void AddSC_boss_maiden_of_virtue() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_maiden_of_virtue"; + newscript->GetAI = GetAI_boss_maiden_of_virtue; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_midnight.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_midnight.cpp new file mode 100644 index 00000000000..d70c25d15db --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_midnight.cpp @@ -0,0 +1,371 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Midnight +SD%Complete: 100 +SDComment: +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" + +#define SAY_MIDNIGHT_KILL "Well done Midnight!" +#define SOUND_MIDNIGHT_KILL 9173 + +#define SAY_APPEAR1 "Cowards! Wretches!" +#define SOUND_APPEAR1 9167 +#define SAY_APPEAR2 "Who dares attack the steed of the Huntsman?" +#define SOUND_APPEAR2 9298 +#define SAY_APPEAR3 "Perhaps you would rather test yourselves against a more formidable opponent?! " +#define SOUND_APPEAR3 9299 + +#define SAY_MOUNT "Come, Midnight, let\'s disperse this petty rabble! " +#define SOUND_MOUNT 9168 + +#define SAY_KILL1 "It was... inevitable." +#define SOUND_KILL1 9169 +#define SAY_KILL2 "Another trophy to add to my collection!" +#define SOUND_KILL2 9300 + +#define SAY_DISARMED "Weapons are merely a convenience for a warrior of my skill!" +#define SOUND_DISARMED 9166 + +#define SAY_DEATH "I always knew... someday I would become... the hunted." +#define SOUND_DEATH 9165 + +#define SAY_RANDOM1 "Such easy sport." +#define SOUND_RANDOM1 9170 +#define SAY_RANDOM2 "Amateurs! Do not think you can best me! I kill for a living." +#define SOUND_RANDOM2 9304 + +#define SPELL_SHADOWCLEAVE 29832 +#define SPELL_INTANGIBLE_PRESENCE 29833 +#define SPELL_BERSERKER_CHARGE 26561 //Only when mounted + +#define MOUNTED_DISPLAYID 16040 + +//Attumen (TODO: Use the summoning spell instead of creature id. It works , but is not convenient for us) +#define SUMMON_ATTUMEN 15550 + +struct MANGOS_DLL_DECL boss_midnightAI : public ScriptedAI +{ + boss_midnightAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint64 Attumen; + uint8 Phase; + uint32 Mount_Timer; + + void Reset() + { + Phase = 1; + Attumen = 0; + Mount_Timer = 0; + + m_creature->SetVisibility(VISIBILITY_ON); + } + + void Aggro(Unit* who) {} + + void KilledUnit(Unit *victim) + { + if(Phase == 2) + { + Unit *pUnit = Unit::GetUnit(*m_creature, Attumen); + if(pUnit) + { + pUnit->MonsterYell(SAY_MIDNIGHT_KILL, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(pUnit, SOUND_MIDNIGHT_KILL); + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(Phase == 1 && (m_creature->GetHealth()*100)/m_creature->GetMaxHealth() < 95) + { + Phase = 2; + Creature *pAttumen = DoSpawnCreature(SUMMON_ATTUMEN, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 45000); + if(pAttumen) + { + Attumen = pAttumen->GetGUID(); + pAttumen->AI()->AttackStart(m_creature->getVictim()); + SetMidnight(pAttumen, m_creature->GetGUID()); + switch(rand()%3) + { + case 0: + pAttumen->Yell(SAY_APPEAR1, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(m_creature, SOUND_APPEAR1); + break; + case 1: + pAttumen->Yell(SAY_APPEAR2, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(m_creature, SOUND_APPEAR2); + break; + case 2: + pAttumen->Yell(SAY_APPEAR3, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(m_creature, SOUND_APPEAR3); + break; + } + } + } + else if(Phase == 2 && (m_creature->GetHealth()*100)/m_creature->GetMaxHealth() < 25) + { + Unit *pAttumen = Unit::GetUnit(*m_creature, Attumen); + if(pAttumen) + Mount(pAttumen); + } + else if(Phase ==3) + { + if(Mount_Timer) + if(Mount_Timer <= diff) + { + Mount_Timer = 0; + m_creature->SetVisibility(VISIBILITY_OFF); + m_creature->GetMotionMaster()->MoveIdle(); + Unit *pAttumen = Unit::GetUnit(*m_creature, Attumen); + if(pAttumen) + { + pAttumen->SetUInt32Value(UNIT_FIELD_DISPLAYID, MOUNTED_DISPLAYID); + pAttumen->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + if(pAttumen->getVictim()) + { + pAttumen->GetMotionMaster()->MoveChase(pAttumen->getVictim()); + pAttumen->SetUInt64Value(UNIT_FIELD_TARGET, pAttumen->getVictim()->GetGUID()); + } + pAttumen->SetFloatValue(OBJECT_FIELD_SCALE_X,1); + } + } else Mount_Timer -= diff; + } + + if(Phase != 3) + DoMeleeAttackIfReady(); + } + + void Mount(Unit *pAttumen) + { + DoPlaySoundToSet(pAttumen, SOUND_MOUNT); + pAttumen->MonsterYell(SAY_MOUNT, LANG_UNIVERSAL, 0); + Phase = 3; + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pAttumen->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + float angle = m_creature->GetAngle(pAttumen); + float distance = m_creature->GetDistance2d(pAttumen); + float newX = m_creature->GetPositionX() + cos(angle)*(distance/2) ; + float newY = m_creature->GetPositionY() + sin(angle)*(distance/2) ; + float newZ = 50; + //m_creature->Relocate(newX,newY,newZ,angle); + //m_creature->SendMonsterMove(newX, newY, newZ, 0, true, 1000); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MovePoint(0, newX, newY, newZ); + distance += 10; + newX = m_creature->GetPositionX() + cos(angle)*(distance/2) ; + newY = m_creature->GetPositionY() + sin(angle)*(distance/2) ; + pAttumen->GetMotionMaster()->Clear(); + pAttumen->GetMotionMaster()->MovePoint(0, newX, newY, newZ); + //pAttumen->Relocate(newX,newY,newZ,-angle); + //pAttumen->SendMonsterMove(newX, newY, newZ, 0, true, 1000); + Mount_Timer = 1000; + } + + void SetMidnight(Creature *, uint64); //Below .. +}; + +CreatureAI* GetAI_boss_midnight(Creature *_Creature) +{ + return new boss_midnightAI(_Creature); +} + +struct MANGOS_DLL_DECL boss_attumenAI : public ScriptedAI +{ + boss_attumenAI(Creature *c) : ScriptedAI(c) + { + Reset(); + Phase = 1; + + CleaveTimer = 10000 + (rand()%6)*1000; + CurseTimer = 30000; + RandomYellTimer = 30000 + (rand()%31)*1000; //Occasionally yell + ChargeTimer = 20000; + ResetTimer = 0; + } + + uint64 Midnight; + uint8 Phase; + uint32 CleaveTimer; + uint32 CurseTimer; + uint32 RandomYellTimer; + uint32 ChargeTimer; //only when mounted + uint32 ResetTimer; + + void Reset() + { + ResetTimer = 2000; + } + + void Aggro(Unit* who) {} + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL1); + case 1: + DoYell(SAY_KILL2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL2); + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + Unit *pMidnight = Unit::GetUnit(*m_creature, Midnight); + if(pMidnight) + { + pMidnight->DealDamage(pMidnight, pMidnight->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + + void UpdateAI(const uint32 diff) + { + if(ResetTimer) + if(ResetTimer <= diff) + { + ResetTimer = 0; + Unit *pMidnight = Unit::GetUnit(*m_creature, Midnight); + if(pMidnight) + { + pMidnight->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pMidnight->SetVisibility(VISIBILITY_ON); + } + Midnight = 0; + + m_creature->SetVisibility(VISIBILITY_OFF); + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + else ResetTimer -= diff; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE )) + return; + + if(CleaveTimer < diff) + { + Unit *target = m_creature->getVictim(); + DoCast(target, SPELL_SHADOWCLEAVE); + CleaveTimer = 10000 + (rand()%6)*1000; + } else CleaveTimer -= diff; + + if(CurseTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_INTANGIBLE_PRESENCE); + CurseTimer = 30000; + } else CurseTimer -= diff; + + if(RandomYellTimer < diff) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_RANDOM1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_RANDOM1); + break; + case 1: + DoYell(SAY_RANDOM2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_RANDOM2); + break; + } + RandomYellTimer = 30000 + (rand()%31)*1000; + } else RandomYellTimer -= diff; + + if(m_creature->GetUInt32Value(UNIT_FIELD_DISPLAYID) == MOUNTED_DISPLAYID) + { + if(ChargeTimer < diff) + { + Unit *target; + std::list t_list = m_creature->getThreatManager().getThreatList(); + std::vector target_list; + for(std::list::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr) + { + target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + if(target && target->GetDistance2d(m_creature) > 5) + target_list.push_back(target); + target = NULL; + } + if(target_list.size()) + target = *(target_list.begin()+rand()%target_list.size()); + + DoCast(target, SPELL_BERSERKER_CHARGE); + ChargeTimer = 20000; + } else ChargeTimer -= diff; + } + else + { + if( (m_creature->GetHealth()*100)/m_creature->GetMaxHealth() < 25) + { + Creature *pMidnight = (Creature*)Unit::GetUnit(*m_creature, Midnight); + if(pMidnight && pMidnight->GetTypeId() == TYPEID_UNIT) + { + ((boss_midnightAI*)(pMidnight->AI()))->Mount(m_creature); + m_creature->SetHealth(pMidnight->GetHealth()); + } + } + } + + DoMeleeAttackIfReady(); + } + + void SpellHit(Unit *source, const SpellEntry *spell) + { + if(spell->Mechanic == MECHANIC_DISARM) + { + DoYell(SAY_DISARMED, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DISARMED); + } + } +}; + +void boss_midnightAI::SetMidnight(Creature *pAttumen, uint64 value) +{ + ((boss_attumenAI*)pAttumen->AI())->Midnight = value; +} + +CreatureAI* GetAI_boss_attumen(Creature *_Creature) +{ + return new boss_attumenAI (_Creature); +} + +void AddSC_boss_attumen() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_attumen"; + newscript->GetAI = GetAI_boss_attumen; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_midnight"; + newscript->GetAI = GetAI_boss_midnight; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_moroes.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_moroes.cpp new file mode 100644 index 00000000000..538de015315 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_moroes.cpp @@ -0,0 +1,868 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Moroes +SD%Complete: 100 +SDComment: +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "def_karazhan.h" + +#define SAY_AGGRO "Hmm, unannounced visitors? Preparations must be made." +#define SOUND_AGGRO 9211 + +#define SAY_SPECIAL_1 "Now, where was I? Oh yes..." +#define SOUND_SPECIAL_1 9215 + +#define SAY_SPECIAL_2 "You rang?" +#define SOUND_SPECIAL_2 9316 + +#define SAY_KILL_1 "One more for dinner this evening" +#define SOUND_KILL_1 9214 + +#define SAY_KILL_2 "Time... Never enough time." +#define SOUND_KILL_2 9314 + +#define SAY_KILL_3 "I've gone and made a mess." +#define SOUND_KILL_3 9315 + +#define SAY_DEATH "How terribly clumsy of me..." +#define SOUND_DEATH 9213 + +#define SPELL_VANISH 24699 +#define SPELL_GARROTE 37066 +#define SPELL_BLIND 34654 +#define SPELL_GOUGE 28456 +#define SPELL_ENRAGE 37023 + +#define ORIENT 4.5784 +#define POS_Z 81.73 + +float Locations[4][2]= +{ + {-10976.2793, -1878.1736}, + {-10979.7587, -1877.8706}, + {-10985.6650, -1877.2458}, + {-10989.1367, -1876.8309}, +}; + +const uint32 Adds[6]= +{ + 17007, + 19872, + 19873, + 19874, + 19875, + 19876, +}; + +struct MANGOS_DLL_DECL boss_moroesAI : public ScriptedAI +{ + boss_moroesAI(Creature *c) : ScriptedAI(c) + { + FirstTime = true; + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint64 AddGUID[4]; + + uint32 Vanish_Timer; + uint32 Blind_Timer; + uint32 Gouge_Timer; + uint32 Wait_Timer; + uint32 CheckAdds_Timer; + uint32 AddId[4]; + + bool FirstTime; + bool InVanish; + bool Enrage; + + void Reset() + { + Vanish_Timer = 30000; + Blind_Timer = 35000; + Gouge_Timer = 23000; + Wait_Timer = 0; + CheckAdds_Timer = 5000; + + Enrage = false; + InVanish = false; + + SpawnAdds(); + + m_creature->setFaction(16); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + if(pInstance) + pInstance->SetData(DATA_MOROES_EVENT, NOT_STARTED); + } + + void StartEvent() + { + if(!pInstance) + return; + + if(pInstance) + pInstance->SetData(DATA_MOROES_EVENT, IN_PROGRESS); + } + + void Aggro(Unit* who) + { + StartEvent(); + + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + AddsAttack(); + } + + void KilledUnit(Unit* victim) + { + switch (rand()%3) + { + case 0: + DoYell(SAY_KILL_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL_1); + break; + case 1: + DoYell(SAY_KILL_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL_2); + break; + case 2: + DoYell(SAY_KILL_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL_3); + break; + } + } + + void JustDied(Unit* victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if(pInstance) + pInstance->SetData(DATA_MOROES_EVENT, DONE); + + DeSpawnAdds(); + } + + uint8 CheckAdd(uint64 guid) + { + Unit* pUnit = Unit::GetUnit((*m_creature), guid); + if(pUnit) + { + if(!pUnit->isAlive()) + return 1; // Exists but is dead + else + return 2; // Exists and is alive + } + + return 0; // Does not exist + } + + void SpawnAdds() + { + Creature *pCreature = NULL; + + if(FirstTime) + { + std::vector AddList; + + for(uint8 i = 0; i < 6; ++i) + AddList.push_back(Adds[i]); + + while(AddList.size() > 4) + AddList.erase((AddList.begin())+(rand()%AddList.size())); + + uint8 j = 0; + for(std::vector::iterator itr = AddList.begin(); itr != AddList.end(); ++itr) + { + uint32 entry = *itr; + + pCreature = m_creature->SummonCreature(entry, Locations[j][0], Locations[j][1], POS_Z, ORIENT, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + if(pCreature) + { + AddGUID[j] = pCreature->GetGUID(); + AddId[j] = entry; + } + ++j; + } + + FirstTime = false; + } + else + { + for(uint8 i = 0; i < 5; ++i) + { + switch(CheckAdd(AddGUID[i])) + { + case 0: + pCreature = m_creature->SummonCreature(AddId[i], Locations[i][0], Locations[i][1], POS_Z, ORIENT, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000); + if(pCreature) + AddGUID[i] = pCreature->GetGUID(); + break; + case 1: + pCreature = ((Creature*)Unit::GetUnit((*m_creature), AddGUID[i])); + if(pCreature) + { + pCreature->Respawn(); + pCreature->AI()->EnterEvadeMode(); + } + break; + case 2: + pCreature = ((Creature*)Unit::GetUnit((*m_creature), AddGUID[i])); + if(!pCreature->IsInEvadeMode()) + pCreature->AI()->EnterEvadeMode(); + break; + } + } + } + } + + void DeSpawnAdds() + { + for(uint8 i = 0; i < 4 ; ++i) + { + Unit* Temp = NULL; + if(AddGUID[i]) + { + Temp = Unit::GetUnit((*m_creature),AddGUID[i]); + if(Temp && Temp->isAlive()) + Temp->DealDamage(Temp, Temp->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + } + + void AddsAttack() + { + for(uint8 i = 0; i < 4; ++i) + { + Unit* Temp = NULL; + if(AddGUID[i]) + { + Temp = Unit::GetUnit((*m_creature),AddGUID[i]); + if(Temp && Temp->isAlive()) + ((Creature*)Temp)->AI()->AttackStart(m_creature->getVictim()); + else + EnterEvadeMode(); + } + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(pInstance && !pInstance->GetData(DATA_MOROES_EVENT)) + EnterEvadeMode(); + + if(!Enrage && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 30) + { + DoCast(m_creature, SPELL_ENRAGE); + Enrage = true; + } + + if(CheckAdds_Timer < diff) + { + for(uint8 i = 0; i < 4; ++i) + { + Unit* Temp = NULL; + if(AddGUID[i]) + { + Temp = Unit::GetUnit((*m_creature),AddGUID[i]); + if(Temp && Temp->isAlive()) + if(!Temp->SelectHostilTarget() || !Temp->getVictim() ) + ((Creature*)Temp)->AI()->AttackStart(m_creature->getVictim()); + } + } + CheckAdds_Timer = 5000; + }else CheckAdds_Timer -= diff; + + //Cast Vanish, then Garrote random victim + if(Vanish_Timer < diff) + { + m_creature->setFaction(35); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + DoCast(m_creature, SPELL_VANISH); + InVanish = true; + Vanish_Timer = 30000; + Wait_Timer = 5000; + }else Vanish_Timer -= diff; + + if(InVanish) + { + if(Wait_Timer < diff) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SPECIAL_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SPECIAL_1); + break; + case 1: + DoYell(SAY_SPECIAL_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SPECIAL_2); + break; + } + + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + target->CastSpell(target, SPELL_GARROTE,true); + + m_creature->setFaction(16); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->AI()->AttackStart(m_creature->getVictim()); + InVanish = false; + }else Wait_Timer -= diff; + } + + //Blind highest aggro, and attack second highest + if(Gouge_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_GOUGE); + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-100); + Gouge_Timer = 40000; + }else Gouge_Timer -= diff; + + if(Blind_Timer < diff) + { + Unit* target = NULL; + std::list t_list = m_creature->getThreatManager().getThreatList(); + + if(t_list.empty()) + return; + + std::vector target_list; + for(std::list::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr) + { + target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + if(target && target->GetDistance2d(m_creature) < 5) + target_list.push_back(target); + } + if(target_list.size()) + target = *(target_list.begin()+rand()%target_list.size()); + + if(target) + DoCast(target, SPELL_BLIND); + + Blind_Timer = 40000; + }else Blind_Timer -= diff; + + if(!InVanish) + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_moroes_guestAI : public ScriptedAI +{ + ScriptedInstance* pInstance; + + uint64 GuestGUID[4]; + + boss_moroes_guestAI(Creature* c) : ScriptedAI(c) + { + for(uint8 i = 0; i < 4; ++i) + GuestGUID[i] = 0; + + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + void Reset() + { + if(pInstance) + pInstance->SetData(DATA_MOROES_EVENT, NOT_STARTED); + } + + void Aggro(Unit* who) {} + + void AcquireGUID() + { + if(!pInstance) + return; + + GuestGUID[0] = pInstance->GetData64(DATA_MOROES); + Creature* Moroes = ((Creature*)Unit::GetUnit((*m_creature), GuestGUID[0])); + if(Moroes) + { + for(uint8 i = 0; i < 3; ++i) + { + uint64 GUID = ((boss_moroesAI*)Moroes->AI())->AddGUID[i]; + if(GUID && GUID != m_creature->GetGUID()) + GuestGUID[i+1] = GUID; + } + } + } + + Unit* SelectTarget() + { + uint64 TempGUID = GuestGUID[rand()%5]; + if(TempGUID) + { + Unit* pUnit = Unit::GetUnit((*m_creature), TempGUID); + if(pUnit && pUnit->isAlive()) + return pUnit; + } + + return m_creature; + } + + void UpdateAI(const uint32 diff) + { + if(pInstance && !pInstance->GetData(DATA_MOROES_EVENT)) + EnterEvadeMode(); + + DoMeleeAttackIfReady(); + } +}; + +#define SPELL_MANABURN 29405 +#define SPELL_MINDFLY 29570 +#define SPELL_SWPAIN 34441 +#define SPELL_SHADOWFORM 29406 + +struct MANGOS_DLL_DECL boss_baroness_dorothea_millstipeAI : public boss_moroes_guestAI +{ + //Shadow Priest + boss_baroness_dorothea_millstipeAI(Creature *c) : boss_moroes_guestAI(c) {} + + uint32 ManaBurn_Timer; + uint32 MindFlay_Timer; + uint32 ShadowWordPain_Timer; + + void Reset() + { + ManaBurn_Timer = 7000; + MindFlay_Timer = 1000; + ShadowWordPain_Timer = 6000; + + DoCast(m_creature,SPELL_SHADOWFORM, true); + + boss_moroes_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_moroes_guestAI::UpdateAI(diff); + + if(MindFlay_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MINDFLY); + MindFlay_Timer = 12000; //3sec channeled + }else MindFlay_Timer -= diff; + + if(ManaBurn_Timer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target && (target->getPowerType() == POWER_MANA)) + DoCast(target,SPELL_MANABURN); + ManaBurn_Timer = 5000; //3 sec cast + }else ManaBurn_Timer -= diff; + + if(ShadowWordPain_Timer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + DoCast(target,SPELL_SWPAIN); + ShadowWordPain_Timer = 7000; + } + }else ShadowWordPain_Timer -= diff; + } +}; + +#define SPELL_HAMMEROFJUSTICE 13005 +#define SPELL_JUDGEMENTOFCOMMAND 29386 +#define SPELL_SEALOFCOMMAND 29385 + +struct MANGOS_DLL_DECL boss_baron_rafe_dreugerAI : public boss_moroes_guestAI +{ + //Retr Pally + boss_baron_rafe_dreugerAI(Creature *c) : boss_moroes_guestAI(c){} + + uint32 HammerOfJustice_Timer; + uint32 SealOfCommand_Timer; + uint32 JudgementOfCommand_Timer; + + void Reset() + { + HammerOfJustice_Timer = 1000; + SealOfCommand_Timer = 7000; + JudgementOfCommand_Timer = SealOfCommand_Timer + 29000; + + boss_moroes_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_moroes_guestAI::UpdateAI(diff); + + if(SealOfCommand_Timer < diff) + { + DoCast(m_creature,SPELL_SEALOFCOMMAND); + SealOfCommand_Timer = 32000; + JudgementOfCommand_Timer = 29000; + }else SealOfCommand_Timer -= diff; + + if(JudgementOfCommand_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_JUDGEMENTOFCOMMAND); + JudgementOfCommand_Timer = SealOfCommand_Timer + 29000; + }else JudgementOfCommand_Timer -= diff; + + if(HammerOfJustice_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HAMMEROFJUSTICE); + HammerOfJustice_Timer = 12000; + }else HammerOfJustice_Timer -= diff; + } +}; + +#define SPELL_DISPELMAGIC 15090 //Self or other guest+Moroes +#define SPELL_GREATERHEAL 29564 //Self or other guest+Moroes +#define SPELL_HOLYFIRE 29563 +#define SPELL_PWSHIELD 29408 + +struct MANGOS_DLL_DECL boss_lady_catriona_von_indiAI : public boss_moroes_guestAI +{ + //Holy Priest + boss_lady_catriona_von_indiAI(Creature *c) : boss_moroes_guestAI(c) {} + + uint32 DispelMagic_Timer; + uint32 GreaterHeal_Timer; + uint32 HolyFire_Timer; + uint32 PowerWordShield_Timer; + + void Reset() + { + DispelMagic_Timer = 11000; + GreaterHeal_Timer = 1500; + HolyFire_Timer = 5000; + PowerWordShield_Timer = 1000; + + AcquireGUID(); + + boss_moroes_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_moroes_guestAI::UpdateAI(diff); + + if(PowerWordShield_Timer < diff) + { + DoCast(m_creature,SPELL_PWSHIELD); + PowerWordShield_Timer = 15000; + }else PowerWordShield_Timer -= diff; + + if(GreaterHeal_Timer < diff) + { + Unit* target = SelectTarget(); + + DoCast(target, SPELL_GREATERHEAL); + GreaterHeal_Timer = 17000; + }else GreaterHeal_Timer -= diff; + + if(HolyFire_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HOLYFIRE); + HolyFire_Timer = 22000; + }else HolyFire_Timer -= diff; + + if(DispelMagic_Timer < diff) + { + if(rand()%2) + { + Unit* target = SelectTarget(); + + DoCast(target, SPELL_DISPELMAGIC); + } + else + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_DISPELMAGIC); + + DispelMagic_Timer = 25000; + }else DispelMagic_Timer -= diff; + } +}; + +#define SPELL_CLEANSE 29380 //Self or other guest+Moroes +#define SPELL_GREATERBLESSOFMIGHT 29381 //Self or other guest+Moroes +#define SPELL_HOLYLIGHT 29562 //Self or other guest+Moroes +#define SPELL_DIVINESHIELD 41367 + +struct MANGOS_DLL_DECL boss_lady_keira_berrybuckAI : public boss_moroes_guestAI +{ + //Holy Pally + boss_lady_keira_berrybuckAI(Creature *c) : boss_moroes_guestAI(c) {} + + uint32 Cleanse_Timer; + uint32 GreaterBless_Timer; + uint32 HolyLight_Timer; + uint32 DivineShield_Timer; + + void Reset() + { + Cleanse_Timer = 13000; + GreaterBless_Timer = 1000; + HolyLight_Timer = 7000; + DivineShield_Timer = 31000; + + AcquireGUID(); + + boss_moroes_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_moroes_guestAI::UpdateAI(diff); + + if(DivineShield_Timer < diff) + { + DoCast(m_creature,SPELL_DIVINESHIELD); + DivineShield_Timer = 31000; + }else DivineShield_Timer -= diff; + + if(HolyLight_Timer < diff) + { + Unit* target = SelectTarget(); + + DoCast(target, SPELL_HOLYLIGHT); + HolyLight_Timer = 10000; + }else HolyLight_Timer -= diff; + + if(GreaterBless_Timer < diff) + { + Unit* target = SelectTarget(); + + DoCast(target, SPELL_GREATERBLESSOFMIGHT); + + GreaterBless_Timer = 50000; + }else GreaterBless_Timer -= diff; + + if(Cleanse_Timer < diff) + { + Unit* target = SelectTarget(); + + DoCast(target, SPELL_CLEANSE); + + Cleanse_Timer = 10000; + }else Cleanse_Timer -= diff; + } +}; + +#define SPELL_HAMSTRING 9080 +#define SPELL_MORTALSTRIKE 29572 +#define SPELL_WHIRLWIND 29573 + +struct MANGOS_DLL_DECL boss_lord_robin_darisAI : public boss_moroes_guestAI +{ + //Arms Warr + boss_lord_robin_darisAI(Creature *c) : boss_moroes_guestAI(c) {} + + uint32 Hamstring_Timer; + uint32 MortalStrike_Timer; + uint32 WhirlWind_Timer; + + void Reset() + { + Hamstring_Timer = 7000; + MortalStrike_Timer = 10000; + WhirlWind_Timer = 21000; + + boss_moroes_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_moroes_guestAI::UpdateAI(diff); + + if(Hamstring_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HAMSTRING); + Hamstring_Timer = 12000; + }else Hamstring_Timer -= diff; + + if(MortalStrike_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_MORTALSTRIKE); + MortalStrike_Timer = 18000; + }else MortalStrike_Timer -= diff; + + if(WhirlWind_Timer < diff) + { + DoCast(m_creature,SPELL_WHIRLWIND); + WhirlWind_Timer = 21000; + }else WhirlWind_Timer -= diff; + } +}; + +#define SPELL_DISARM 8379 +#define SPELL_HEROICSTRIKE 29567 +#define SPELL_SHIELDBASH 11972 +#define SPELL_SHIELDWALL 29390 + +struct MANGOS_DLL_DECL boss_lord_crispin_ferenceAI : public boss_moroes_guestAI +{ + //Arms Warr + boss_lord_crispin_ferenceAI(Creature *c) : boss_moroes_guestAI(c) {} + + uint32 Disarm_Timer; + uint32 HeroicStrike_Timer; + uint32 ShieldBash_Timer; + uint32 ShieldWall_Timer; + + void Reset() + { + Disarm_Timer = 6000; + HeroicStrike_Timer = 10000; + ShieldBash_Timer = 8000; + ShieldWall_Timer = 4000; + + boss_moroes_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_moroes_guestAI::UpdateAI(diff); + + if(Disarm_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_DISARM); + Disarm_Timer = 12000; + }else Disarm_Timer -= diff; + + if(HeroicStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HEROICSTRIKE); + HeroicStrike_Timer = 10000; + }else HeroicStrike_Timer -= diff; + + if(ShieldBash_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHIELDBASH); + ShieldBash_Timer = 13000; + }else ShieldBash_Timer -= diff; + + if(ShieldWall_Timer < diff) + { + DoCast(m_creature,SPELL_SHIELDWALL); + ShieldWall_Timer = 21000; + }else ShieldWall_Timer -= diff; + } +}; + +CreatureAI* GetAI_boss_moroes(Creature *_Creature) +{ + return new boss_moroesAI (_Creature); +} + +CreatureAI* GetAI_baroness_dorothea_millstipe(Creature *_Creature) +{ + return new boss_baroness_dorothea_millstipeAI (_Creature); +} + +CreatureAI* GetAI_baron_rafe_dreuger(Creature *_Creature) +{ + return new boss_baron_rafe_dreugerAI (_Creature); +} + +CreatureAI* GetAI_lady_catriona_von_indi(Creature *_Creature) +{ + return new boss_lady_catriona_von_indiAI (_Creature); +} + +CreatureAI* GetAI_lady_keira_berrybuck(Creature *_Creature) +{ + return new boss_lady_keira_berrybuckAI (_Creature); +} + +CreatureAI* GetAI_lord_robin_daris(Creature *_Creature) +{ + return new boss_lord_robin_darisAI (_Creature); +} + +CreatureAI* GetAI_lord_crispin_ference(Creature *_Creature) +{ + return new boss_lord_crispin_ferenceAI (_Creature); +} + +void AddSC_boss_moroes() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_moroes"; + newscript->GetAI = GetAI_boss_moroes; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_baroness_dorothea_millstipe"; + newscript->GetAI = GetAI_baroness_dorothea_millstipe; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_baron_rafe_dreuger"; + newscript->GetAI = GetAI_baron_rafe_dreuger; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_lady_catriona_von_indi"; + newscript->GetAI = GetAI_lady_catriona_von_indi; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_lady_keira_berrybuck"; + newscript->GetAI = GetAI_lady_keira_berrybuck; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_lord_robin_daris"; + newscript->GetAI = GetAI_lord_robin_daris; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_lord_crispin_ference"; + newscript->GetAI = GetAI_lord_crispin_ference; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_netherspite.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_netherspite.cpp new file mode 100644 index 00000000000..dfddad46aff --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_netherspite.cpp @@ -0,0 +1,37 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Netherspite +SD%Complete: 0 +SDComment: Place Holder +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_NETHERBURN_AURA 30522 +#define SPELL_VOIDZONE 37014 //Probably won't work +#define SPELL_BERSERK 26662 +#define SPELL_NETHERBREATH 36631 + +//Beams (no idea how these are going to work in Mangos) +#define SPELL_DOMINANCE_ENEMY 30423 +#define SPELL_DOMINANCE_SELF 30468 +#define SPELL_PERSEVERANCE_ENEMY 30421 +#define SPELL_PERSEVERANCE_SELF 30466 +#define SPELL_SERENITY_ENEMY 30422 +#define SPELL_SERENITY_SELF 30467 diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_nightbane.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_nightbane.cpp new file mode 100644 index 00000000000..8b84df3a7fd --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_nightbane.cpp @@ -0,0 +1,33 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Nightbane +SD%Complete: 0 +SDComment: Place holder +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_BELLOWING_ROAR 39427 +#define SPELL_CHARRED_EARTH 30129 //Also 30209 (Target Charred Earth) triggers this +#define SPELL_DISTRACTING_ASH 30130 +#define SPELL_SMOLDERING_BREATH 30210 +#define SPELL_TAIL_SWEEP 25653 +#define SPELL_RAIN_OF_BONES 37098 +#define SPELL_SMOKING_BLAST 37057 +#define SPELL_FIREBALL_BARRAGE 30282 diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp new file mode 100644 index 00000000000..842321cfbff --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp @@ -0,0 +1,646 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Prince_Malchezzar +SD%Complete: 100 +SDComment: +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" + +#define SAY_AGGRO "Madness has brought you here to me. I shall be your undoing!" +#define SOUND_AGGRO 9218 +#define SAY_SUMMON1 "You face not Malchezaar alone, but the legions I command!" +#define SOUND_SUMMON1 9322 +#define SAY_SUMMON2 "All realities, all dimensions are open to me!" +#define SOUND_SUMMON2 9224 +#define SAY_PHASE2 "Simple fools! Time is the fire in which you'll burn!" +#define SOUND_PHASE2 9220 +#define SAY_PHASE3 "How can you hope to withstand against such overwhelming power?" +#define SOUND_PHASE3 9321 +#define SAY_PDEATH1 "You are, but a plaything, unfit even to amuse." +#define SOUND_PDEATH1 9319 +#define SAY_PDEATH2 "Your greed, your foolishness has brought you to this end." +#define SOUND_PDEATH2 9318 +#define SAY_PDEATH3 "Surely you did not think you could win." +#define SOUND_PDEATH3 9222 +#define SAY_DEATH "I refuse to concede defeat. I am a prince of the Eredar! I am..." +#define SOUND_DEATH 9221 + +// 19 Coordinates for Infernal spawns +struct InfernalPoint +{ + float x,y; +}; + +#define INFERNAL_Z 275.5 + +static InfernalPoint InfernalPoints[] = +{ + {-10922.8, -1985.2}, + {-10916.2, -1996.2}, + {-10932.2, -2008.1}, + {-10948.8, -2022.1}, + {-10958.7, -1997.7}, + {-10971.5, -1997.5}, + {-10990.8, -1995.1}, + {-10989.8, -1976.5}, + {-10971.6, -1973.0}, + {-10955.5, -1974.0}, + {-10939.6, -1969.8}, + {-10958.0, -1952.2}, + {-10941.7, -1954.8}, + {-10943.1, -1988.5}, + {-10948.8, -2005.1}, + {-10984.0, -2019.3}, + {-10932.8, -1979.6}, + {-10932.8, -1979.6}, + {-10935.7, -1996.0} +}; + +#define TOTAL_INFERNAL_POINTS 19 + +//Enfeeble is supposed to reduce hp to 1 and then heal player back to full when it ends +//Along with reducing healing and regen while enfeebled to 0% +//This spell effect will only reduce healing + +#define SPELL_ENFEEBLE 30843 //Enfeeble during phase 1 and 2 +#define SPELL_ENFEEBLE_EFFECT 41624 + +#define SPELL_SHADOWNOVA 30852 //Shadownova used during all phases +#define SPELL_SW_PAIN 30854 //Shadow word pain during phase 1 and 3 (different targeting rules though) +#define SPELL_THRASH_PASSIVE 12787 //Extra attack chance during phase 2 +#define SPELL_SUNDER_ARMOR 30901 //Sunder armor during phase 2 +#define SPELL_THRASH_AURA 3417 //Passive proc chance for thrash +#define SPELL_EQUIP_AXES 30857 //Visual for axe equiping +#define SPELL_AMPLIFY_DAMAGE 12738 //Amplifiy during phase 3 +#define SPELL_HELLFIRE 30859 //Infenals' hellfire aura +#define NETHERSPITE_INFERNAL 17646 //The netherspite infernal creature +#define MALCHEZARS_AXE 17650 //Malchezar's axes (creatures), summoned during phase 3 + +#define INFERNAL_MODEL_INVISIBLE 11686 //Infernal Effects +#define SPELL_INFERNAL_RELAY 30834 + +#define AXE_EQUIP_MODEL 40066 //Axes info +#define AXE_EQUIP_INFO 33448898 + +//---------Infernal code first +struct MANGOS_DLL_DECL netherspite_infernalAI : public ScriptedAI +{ + netherspite_infernalAI(Creature *c) : ScriptedAI(c) , + malchezaar(0), HellfireTimer(0), CleanupTimer(0), point(NULL) {Reset();} + + uint32 HellfireTimer; + uint32 CleanupTimer; + uint32 malchezaar; + InfernalPoint *point; + + void Reset() {} + void Aggro(Unit *who) {} + void MoveInLineOfSight(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if(HellfireTimer) + if(HellfireTimer <= diff) + { + DoCast(m_creature, SPELL_HELLFIRE); + HellfireTimer = 0; + } + else HellfireTimer -= diff; + + if(CleanupTimer) + if(CleanupTimer <= diff) + { + Cleanup(); + CleanupTimer = 0; + } else CleanupTimer -= diff; + } + + void KilledUnit(Unit *who) + { + Unit *pMalchezaar = Unit::GetUnit(*m_creature, malchezaar); + if(pMalchezaar) + ((Creature*)pMalchezaar)->AI()->KilledUnit(who); + } + + void SpellHit(Unit *who, const SpellEntry *spell) + { + if(spell->Id == SPELL_INFERNAL_RELAY) + { + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, m_creature->GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID)); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + HellfireTimer = 4000; + CleanupTimer = 170000; + } + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if(done_by->GetGUID() != malchezaar) + damage = 0; + } + + void Cleanup(); //below ... +}; + +struct MANGOS_DLL_DECL boss_malchezaarAI : public ScriptedAI +{ + boss_malchezaarAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 EnfeebleTimer; + uint32 EnfeebleResetTimer; + uint32 ShadowNovaTimer; + uint32 SWPainTimer; + uint32 SunderArmorTimer; + uint32 AmplifyDamageTimer; + uint32 InfernalTimer; + uint32 AxesTargetSwitchTimer; + uint32 InfernalCleanupTimer; + + std::vector infernals; + std::vector positions; + + uint64 axes[2]; + uint64 enfeeble_targets[5]; + uint64 enfeeble_health[5]; + + uint32 phase; + + void Reset() + { + AxesCleanup(); + ClearWeapons(); + InfernalCleanup(); + positions.clear(); + + for(int i =0; i < 5; ++i) + enfeeble_targets[i] = 0; + + for(int i = 0; i < TOTAL_INFERNAL_POINTS; ++i) + positions.push_back(&InfernalPoints[i]); + + EnfeebleTimer = 30000; + EnfeebleResetTimer = 38000; + ShadowNovaTimer = 35000; + SWPainTimer = 20000; + AmplifyDamageTimer = 10000; + InfernalTimer = 45000; + InfernalCleanupTimer = 47000; + AxesTargetSwitchTimer = 7500 + rand()%12500; + phase = 1; + } + + void KilledUnit(Unit *victim) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_PDEATH1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(victim, SOUND_PDEATH1); + break; + case 1: + DoYell(SAY_PDEATH2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(victim, SOUND_PDEATH2); + break; + case 2: + DoYell(SAY_PDEATH3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(victim, SOUND_PDEATH3); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + AxesCleanup(); + ClearWeapons(); + InfernalCleanup(); + positions.clear(); + + for(int i = 0; i < TOTAL_INFERNAL_POINTS; ++i) + positions.push_back(&InfernalPoints[i]); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + + void InfernalCleanup() + { + //Infernal Cleanup + for(std::vector::iterator itr = infernals.begin(); itr!= infernals.end(); ++itr) + { + Unit *pInfernal = Unit::GetUnit(*m_creature, *itr); + if(pInfernal && pInfernal->isAlive()) + { + pInfernal->SetVisibility(VISIBILITY_OFF); + pInfernal->setDeathState(JUST_DIED); + } + } + infernals.clear(); + } + + void AxesCleanup() + { + for(int i=0; i<2;++i) + { + Unit *axe = Unit::GetUnit(*m_creature, axes[i]); + if(axe && axe->isAlive()) + axe->DealDamage(axe, axe->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + axes[i] = 0; + } + } + + void ClearWeapons() + { + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 0); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO, 0); + + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, 0); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO+2, 0); + + //damage + const CreatureInfo *cinfo = m_creature->GetCreatureInfo(); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, cinfo->mindmg); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, cinfo->maxdmg); + m_creature->UpdateDamagePhysical(BASE_ATTACK); + } + + void EnfeebleHealthEffect() + { + const SpellEntry *info = GetSpellStore()->LookupEntry(SPELL_ENFEEBLE_EFFECT); + if(!info) + return; + + std::list t_list = m_creature->getThreatManager().getThreatList(); + std::vector targets; + + if(!t_list.size()) + return; + + //begin + 1 , so we don't target the one with the highest threat + std::list::iterator itr = t_list.begin(); + std::advance(itr, 1); + for( ; itr!= t_list.end(); ++itr) //store the threat list in a different container + { + Unit *target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + //only on alive players + if(target && target->isAlive() && target->GetTypeId() == TYPEID_PLAYER ) + targets.push_back( target); + } + + //cut down to size if we have more than 5 targets + while(targets.size() > 5) + targets.erase(targets.begin()+rand()%targets.size()); + + int i = 0; + for(std::vector::iterator itr = targets.begin(); itr!= targets.end(); ++itr, ++i) + { + Unit *target = *itr; + if(target) + { + enfeeble_targets[i] = target->GetGUID(); + enfeeble_health[i] = target->GetHealth(); + + target->CastSpell(target, SPELL_ENFEEBLE, true, 0, 0, m_creature->GetGUID()); + target->SetHealth(1); + } + } + + } + + void EnfeebleResetHealth() + { + for(int i = 0; i < 5; ++i) + { + Unit *target = Unit::GetUnit(*m_creature, enfeeble_targets[i]); + if(target && target->isAlive()) + target->SetHealth(enfeeble_health[i]); + enfeeble_targets[i] = 0; + enfeeble_health[i] = 0; + } + } + + void SummonInfernal(const uint32 diff) + { + InfernalPoint *point = NULL; + float posX,posY,posZ; + if( (m_creature->GetMapId() != 532) || positions.empty()) + { + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 60, posX, posY, posZ); + } + else + { + std::vector::iterator itr = positions.begin()+rand()%positions.size(); + point = *itr; + positions.erase(itr); + + posX = point->x; + posY = point->y; + posZ = INFERNAL_Z; + } + + Creature *Infernal = m_creature->SummonCreature(NETHERSPITE_INFERNAL, posX, posY, posZ, 0, TEMPSUMMON_TIMED_DESPAWN, 180000); + + if (Infernal) + { + Infernal->SetUInt32Value(UNIT_FIELD_DISPLAYID, INFERNAL_MODEL_INVISIBLE); + Infernal->setFaction(m_creature->getFaction()); + if(point) + ((netherspite_infernalAI*)Infernal->AI())->point=point; + ((netherspite_infernalAI*)Infernal->AI())->malchezaar=m_creature->GetGUID(); + + infernals.push_back(Infernal->GetGUID()); + DoCast(Infernal, SPELL_INFERNAL_RELAY); + } + + switch(rand()%2) + { + case 0: + DoYell(SAY_SUMMON1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SUMMON1); + break; + case 1: + DoYell(SAY_SUMMON2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SUMMON2); + break; + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(EnfeebleResetTimer) + if(EnfeebleResetTimer <= diff) //Let's not forget to reset that + { + EnfeebleResetHealth(); + EnfeebleResetTimer=0; + }else EnfeebleResetTimer -= diff; + + if(m_creature->hasUnitState(UNIT_STAT_STUNDED)) //While shifting to phase 2 malchezaar stuns himself + return; + + if(m_creature->GetUInt64Value(UNIT_FIELD_TARGET)!=m_creature->getVictim()->GetGUID()) + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->getVictim()->GetGUID()); + + if(phase == 1) + { + if( (m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 60) + { + m_creature->InterruptNonMeleeSpells(false); + + phase = 2; + + //animation + DoCast(m_creature, SPELL_EQUIP_AXES); + + //text + DoYell(SAY_PHASE2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE2); + + //passive thrash aura + m_creature->CastSpell(m_creature, SPELL_THRASH_AURA, true); + + //models + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, AXE_EQUIP_MODEL); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO, AXE_EQUIP_INFO); + + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY+1, AXE_EQUIP_MODEL); + m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO+2, AXE_EQUIP_INFO); + + //damage + const CreatureInfo *cinfo = m_creature->GetCreatureInfo(); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, 2*cinfo->mindmg); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, 2*cinfo->maxdmg); + m_creature->UpdateDamagePhysical(BASE_ATTACK); + + m_creature->SetBaseWeaponDamage(OFF_ATTACK, MINDAMAGE, cinfo->mindmg); + m_creature->SetBaseWeaponDamage(OFF_ATTACK, MAXDAMAGE, cinfo->maxdmg); + //Sigh, updating only works on main attack , do it manually .... + m_creature->SetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE, cinfo->mindmg); + m_creature->SetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE, cinfo->maxdmg); + + m_creature->SetAttackTime(OFF_ATTACK, (((uint32)m_creature->GetAttackTime(BASE_ATTACK)/1.5))); + } + } + else if(phase == 2) + { + if( (m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 30) + { + InfernalTimer = 15000; + + phase = 3; + + ClearWeapons(); + + //remove thrash + m_creature->RemoveAurasDueToSpell(SPELL_THRASH_AURA); + + DoYell(SAY_PHASE3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE3); + + Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0); + for(uint32 i=0; i<2; ++i) + { + Creature *axe = m_creature->SummonCreature(MALCHEZARS_AXE, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1000); + if(axe) + { + axe->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, AXE_EQUIP_MODEL); + axe->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO, AXE_EQUIP_INFO); + + axe->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + axe->setFaction(m_creature->getFaction()); + axes[i] = axe->GetGUID(); + if(target) + { + axe->AI()->AttackStart(target); + // axe->getThreatManager().tauntApply(target); //Taunt Apply and fade out does not work properly + // So we'll use a hack to add a lot of threat to our target + axe->AddThreat(target, 10000000.0f); + } + } + } + + if(ShadowNovaTimer > 35000) + ShadowNovaTimer = EnfeebleTimer + 5000; + + return; + } + + if(SunderArmorTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SUNDER_ARMOR); + SunderArmorTimer = 15000; + + }else SunderArmorTimer -= diff; + } + else + { + if(AxesTargetSwitchTimer < diff) + { + AxesTargetSwitchTimer = 7500 + rand()%12500 ; + + Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + for(int i = 0; i < 2; ++i) + { + Unit *axe = Unit::GetUnit(*m_creature, axes[i]); + if(axe) + { + float threat = 1000000.0f; + if(axe->getVictim() && m_creature->getThreatManager().getThreat(axe->getVictim())) + { + threat = axe->getThreatManager().getThreat(axe->getVictim()); + axe->getThreatManager().modifyThreatPercent(axe->getVictim(), -100); + } + if(target) + axe->AddThreat(target, threat); + //axe->getThreatManager().tauntFadeOut(axe->getVictim()); + //axe->getThreatManager().tauntApply(target); + } + } + } + } else AxesTargetSwitchTimer -= diff; + + if(AmplifyDamageTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_AMPLIFY_DAMAGE); + AmplifyDamageTimer = 20000 + rand()%10000; + }else AmplifyDamageTimer -= diff; + } + + //Time for global and double timers + if(InfernalTimer < diff) + { + SummonInfernal(diff); + InfernalTimer = phase == 3 ? 15000 : 45000; //15 secs in phase 3, 45 otherwise + }else InfernalTimer -= diff; + + if(ShadowNovaTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SHADOWNOVA); + ShadowNovaTimer = phase == 3 ? 35000 : -1; + }else ShadowNovaTimer -= diff; + + if(phase != 2) + { + if(SWPainTimer < diff) + { + Unit *target; + if(phase == 1) + target = m_creature->getVictim(); // the tank + else //anyone but the tank + target = SelectUnit(SELECT_TARGET_RANDOM, 1); + + DoCast(target, SPELL_SW_PAIN); + SWPainTimer = 20000; + }else SWPainTimer -= diff; + } + + if(phase != 3) + { + if(EnfeebleTimer < diff) + { + EnfeebleHealthEffect(); + EnfeebleTimer = 30000; + ShadowNovaTimer = 5000; + EnfeebleResetTimer = 9000; + }else EnfeebleTimer -= diff; + } + + if(phase==2) + DoMeleeAttacksIfReady(); + else + DoMeleeAttackIfReady(); + } + + void DoMeleeAttacksIfReady() + { + if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE) && !m_creature->IsNonMeleeSpellCasted(false)) + { + //Check for base attack + if( m_creature->isAttackReady()) + { + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); + } + //Check for offhand attack + if( m_creature->isAttackReady(OFF_ATTACK)) + { + m_creature->AttackerStateUpdate(m_creature->getVictim(), OFF_ATTACK); + m_creature->resetAttackTimer(OFF_ATTACK); + } + } + } + + void Cleanup(Creature *infernal, InfernalPoint *point) + { + for(std::vector::iterator itr = infernals.begin(); itr!= infernals.end(); ++itr) + if(*itr == infernal->GetGUID()) + { + infernals.erase(itr); + break; + } + + positions.push_back(point); + } +}; + +void netherspite_infernalAI::Cleanup() +{ + Unit *pMalchezaar = Unit::GetUnit(*m_creature, malchezaar); + + if(pMalchezaar && pMalchezaar->isAlive()) + ((boss_malchezaarAI*)((Creature*)pMalchezaar)->AI())->Cleanup(m_creature, point); +} + +CreatureAI* GetAI_netherspite_infernal(Creature *_Creature) +{ + return new netherspite_infernalAI (_Creature); +} + +CreatureAI* GetAI_boss_malchezaar(Creature *_Creature) +{ + return new boss_malchezaarAI (_Creature); +} + +void AddSC_netherspite_infernal() +{ + Script *newscript; + newscript = new Script; + newscript->Name="netherspite_infernal"; + newscript->GetAI = GetAI_netherspite_infernal; + m_scripts[nrscripts++] = newscript; +} + +void AddSC_boss_malchezaar() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_malchezaar"; + newscript->GetAI = GetAI_boss_malchezaar; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_shade_of_aran.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_shade_of_aran.cpp new file mode 100644 index 00000000000..c90620f04a7 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_shade_of_aran.cpp @@ -0,0 +1,673 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Shade_of_Aran +SD%Complete: 95 +SDComment: Flame wreath missing cast animation, mods won't triggere. Drinking may cause client crash (core related) +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "../../creature/simple_ai.h" +#include "def_karazhan.h" +#include "GameObject.h" + +//Aggro +#define SAY_AGGRO1 "Please, no more. My son... he's gone mad!" +#define SOUND_AGGRO1 9241 + +#define SAY_AGGRO2 "I'll not be tortured again!" +#define SOUND_AGGRO2 9323 + +#define SAY_AGGRO3 "Who are you? What do you want? Stay away from me!" +#define SOUND_AGGRO3 9324 + +//Flame Wreath +#define SAY_FLAMEWREATH1 "I'll show you this beaten dog still has some teeth!" +#define SOUND_FLAMEWREATH1 9245 +#define SAY_FLAMEWREATH2 "Burn you hellish fiends!" +#define SOUND_FLAMEWREATH2 9326 + +//Blizzard +#define SAY_BLIZZARD1 "I'll freeze you all!" +#define SOUND_BLIZZARD1 9246 +#define SAY_BLIZZARD2 "Back to the cold dark with you!" +#define SOUND_BLIZZARD2 9327 + +//Arcane Explosion +#define SAY_EXPLOSION1 "Yes, yes, my son is quite powerful... but I have powers of my own!" +#define SOUND_EXPLOSION1 9242 +#define SAY_EXPLOSION2 "I am not some simple jester! I am Nielas Aran!" +#define SOUND_EXPLOSION2 9325 + +//Low Mana / AoE Pyroblast +#define SAY_DRINK "Surely you would not deny an old man a replenishing drink? No, no I thought not." +#define SOUND_DRINK 9248 + +//Summon Water Elementals +#define SAY_ELEMENTALS "I'm not finished yet! No, I have a few more tricks up me sleeve." +#define SOUND_ELEMENTALS 9251 + +//Player Death +#define SAY_KILL1 "I want this nightmare to be over!" +#define SOUND_KILL1 9250 + +#define SAY_KILL2 "Torment me no more!" +#define SOUND_KILL2 9328 + +//Time over +#define SAY_TIMEOVER "You've wasted enough of my time. Let these games be finished!" +#define SOUND_TIMEOVER 9247 + +//Aran's death +#define SAY_DEATH "At last... The nightmare is.. over..." +#define SOUND_DEATH 9244 + +//Atiesh is equipped by a raid member +#define SAY_ATIESH "Where did you get that?! Did HE send you?!" +#define SOUND_ATIESH 9249 + +//Spells +#define SPELL_FROSTBOLT 29954 +#define SPELL_FIREBALL 29953 +#define SPELL_ARCMISSLE 29955 +#define SPELL_CHAINSOFICE 29991 +#define SPELL_DRAGONSBREATH 29964 +#define SPELL_MASSSLOW 30035 +#define SPELL_FLAME_WREATH 29946 +#define SPELL_AOE_CS 29961 +#define SPELL_PLAYERPULL 32265 +#define SPELL_AEXPLOSION 29973 +#define SPELL_MASS_POLY 29963 +#define SPELL_BLINK_CENTER 29967 +#define SPELL_ELEMENTALS 29962 +#define SPELL_CONJURE 29975 +#define SPELL_DRINK 30024 +#define SPELL_POTION 32453 +#define SPELL_AOE_PYROBLAST 29978 + +//Creature Spells +#define SPELL_CIRCULAR_BLIZZARD 29951 //29952 is the REAL circular blizzard that leaves persistant blizzards that last for 10 seconds +#define SPELL_WATERBOLT 31012 +#define SPELL_SHADOW_PYRO 29978 + +//Creatures +#define CREATURE_WATER_ELEMENTAL 17167 +#define CREATURE_SHADOW_OF_ARAN 18254 +#define CREATURE_ARAN_BLIZZARD 17161 + +enum SuperSpell +{ + SUPER_FLAME = 0, + SUPER_BLIZZARD, + SUPER_AE, +}; + +struct MANGOS_DLL_DECL boss_aranAI : public ScriptedAI +{ + boss_aranAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 SecondarySpellTimer; + uint32 NormalCastTimer; + uint32 SuperCastTimer; + uint32 BerserkTimer; + uint32 CloseDoorTimer; // Don't close the door right on aggro in case some people are still entering. + + uint8 LastSuperSpell; + + uint32 FlameWreathTimer; + uint32 FlameWreathCheckTime; + uint64 FlameWreathTarget[3]; + float FWTargPosX[3]; + float FWTargPosY[3]; + + uint32 CurrentNormalSpell; + uint32 ArcaneCooldown; + uint32 FireCooldown; + uint32 FrostCooldown; + + uint32 DrinkInturruptTimer; + + bool ElementalsSpawned; + bool Drinking; + bool DrinkInturrupted; + + void Reset() + { + SecondarySpellTimer = 5000; + NormalCastTimer = 0; + SuperCastTimer = 35000; + BerserkTimer = 720000; + CloseDoorTimer = 15000; + + LastSuperSpell = rand()%3; + + FlameWreathTimer = 0; + FlameWreathCheckTime = 0; + + CurrentNormalSpell = 0; + ArcaneCooldown = 0; + FireCooldown = 0; + FrostCooldown = 0; + + DrinkInturruptTimer = 10000; + + ElementalsSpawned = false; + Drinking = false; + DrinkInturrupted = false; + + if(pInstance) + { + // Not in progress + pInstance->SetData(DATA_SHADEOFARAN_EVENT, NOT_STARTED); + + if(GameObject* Door = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_GAMEOBJECT_LIBRARY_DOOR))) + Door->SetGoState(0); + } + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(victim, SOUND_KILL1); + break; + case 1: + DoYell(SAY_KILL2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(victim, SOUND_KILL2); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(NULL, SOUND_DEATH); + + if(pInstance) + { + pInstance->SetData(DATA_SHADEOFARAN_EVENT, DONE); + if(GameObject* Door = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_GAMEOBJECT_LIBRARY_DOOR))) + Door->SetGoState(0); + } + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO3); + break; + } + + if(pInstance) + pInstance->SetData(DATA_SHADEOFARAN_EVENT, IN_PROGRESS); + } + + void FlameWreathEffect() + { + std::vector targets; + std::list t_list = m_creature->getThreatManager().getThreatList(); + + if(!t_list.size()) + return; + + //store the threat list in a different container + for(std::list::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr) + { + Unit *target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + //only on alive players + if(target && target->isAlive() && target->GetTypeId() == TYPEID_PLAYER ) + targets.push_back( target); + } + + //cut down to size if we have more than 3 targets + while(targets.size() > 3) + targets.erase(targets.begin()+rand()%targets.size()); + + uint32 i = 0; + for(std::vector::iterator itr = targets.begin(); itr!= targets.end(); ++itr) + { + if(*itr) + { + FlameWreathTarget[i] = (*itr)->GetGUID(); + FWTargPosX[i] = (*itr)->GetPositionX(); + FWTargPosY[i] = (*itr)->GetPositionY(); + m_creature->CastSpell((*itr), SPELL_FLAME_WREATH, true); + i++; + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(CloseDoorTimer) + if(CloseDoorTimer <= diff) + { + if(pInstance) + if(GameObject* Door = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_GAMEOBJECT_LIBRARY_DOOR))) + Door->SetGoState(1); + CloseDoorTimer = 0; + } + else CloseDoorTimer -= diff; + + //Cooldowns for casts + if (ArcaneCooldown) + if (ArcaneCooldown >= diff) + ArcaneCooldown -= diff; + else ArcaneCooldown = 0; + + if (FireCooldown) + if (FireCooldown >= diff) + FireCooldown -= diff; + else FireCooldown = 0; + + if (FrostCooldown) + if (FrostCooldown >= diff) + FrostCooldown -= diff; + else FrostCooldown = 0; + + if(!Drinking && m_creature->GetMaxPower(POWER_MANA) && (m_creature->GetPower(POWER_MANA)*100 / m_creature->GetMaxPower(POWER_MANA)) < 20) + { + Drinking = true; + m_creature->InterruptNonMeleeSpells(false); + + DoYell(SAY_DRINK, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DRINK); + + if (!DrinkInturrupted) + { + m_creature->CastSpell(m_creature, SPELL_MASS_POLY, true); + m_creature->CastSpell(m_creature, SPELL_CONJURE, false); + m_creature->CastSpell(m_creature, SPELL_DRINK, false); + //Sitting down + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 1); + DrinkInturruptTimer = 10000; + } + } + + //Drink Inturrupt + if (Drinking && DrinkInturrupted) + { + Drinking = false; + m_creature->RemoveAurasDueToSpell(SPELL_DRINK); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + m_creature->SetPower(POWER_MANA, m_creature->GetMaxPower(POWER_MANA)-32000); + m_creature->CastSpell(m_creature, SPELL_POTION, false); + } + + //Drink Inturrupt Timer + if (Drinking && !DrinkInturrupted) + if (DrinkInturruptTimer >= diff) + DrinkInturruptTimer -= diff; + else + { + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + m_creature->CastSpell(m_creature, SPELL_POTION, true); + m_creature->CastSpell(m_creature, SPELL_AOE_PYROBLAST, false); + DrinkInturrupted = true; + Drinking = false; + } + + //Don't execute any more code if we are drinking + if (Drinking) + return; + + //Normal casts + if(NormalCastTimer < diff) + { + if (!m_creature->IsNonMeleeSpellCasted(false)) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (!target) + return; + + uint32 Spells[3]; + uint8 AvailableSpells = 0; + + //Check for what spells are not on cooldown + if (!ArcaneCooldown) + { + Spells[AvailableSpells] = SPELL_ARCMISSLE; + AvailableSpells++; + } + if (!FireCooldown) + { + Spells[AvailableSpells] = SPELL_FIREBALL; + AvailableSpells++; + } + if (!FrostCooldown) + { + Spells[AvailableSpells] = SPELL_FROSTBOLT; + AvailableSpells++; + } + + //If no available spells wait 1 second and try again + if (AvailableSpells) + { + CurrentNormalSpell = Spells[rand() % AvailableSpells]; + DoCast(target, CurrentNormalSpell); + } + } + NormalCastTimer = 1000; + }else NormalCastTimer -= diff; + + if(SecondarySpellTimer < diff) + { + switch (rand()%2) + { + + case 0: + DoCast(m_creature, SPELL_AOE_CS); + break; + case 1: + Unit* pUnit; + pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (pUnit) + DoCast(pUnit, SPELL_CHAINSOFICE); + break; + } + SecondarySpellTimer = 5000 + (rand()%15000); + }else SecondarySpellTimer -= diff; + + if(SuperCastTimer < diff) + { + uint8 Available[2]; + + switch (LastSuperSpell) + { + case SUPER_AE: + Available[0] = SUPER_FLAME; + Available[1] = SUPER_BLIZZARD; + break; + case SUPER_FLAME: + Available[0] = SUPER_AE; + Available[1] = SUPER_BLIZZARD; + break; + case SUPER_BLIZZARD: + Available[0] = SUPER_FLAME; + Available[1] = SUPER_AE; + break; + } + + LastSuperSpell = Available[rand()%2]; + + switch (LastSuperSpell) + { + case SUPER_AE: + + if (rand()%2) + { + DoYell(SAY_EXPLOSION1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_EXPLOSION1); + }else + { + DoYell(SAY_EXPLOSION2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_EXPLOSION2); + } + + m_creature->CastSpell(m_creature, SPELL_BLINK_CENTER, true); + m_creature->CastSpell(m_creature, SPELL_PLAYERPULL, true); + m_creature->CastSpell(m_creature, SPELL_MASSSLOW, true); + m_creature->CastSpell(m_creature, SPELL_AEXPLOSION, false); + break; + + case SUPER_FLAME: + if (rand()%2) + { + DoYell(SAY_FLAMEWREATH1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_FLAMEWREATH1); + }else + { + DoYell(SAY_FLAMEWREATH2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_FLAMEWREATH2); + } + + FlameWreathTimer = 20000; + FlameWreathCheckTime = 500; + + FlameWreathTarget[0] = 0; + FlameWreathTarget[1] = 0; + FlameWreathTarget[2] = 0; + + FlameWreathEffect(); + break; + + case SUPER_BLIZZARD: + + if (rand()%2) + { + DoYell(SAY_BLIZZARD1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_BLIZZARD1); + }else + { + DoYell(SAY_BLIZZARD2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_BLIZZARD2); + } + + Creature* Spawn = NULL; + Spawn = DoSpawnCreature(CREATURE_ARAN_BLIZZARD, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 25000); + if (Spawn) + { + Spawn->setFaction(m_creature->getFaction()); + Spawn->CastSpell(Spawn, SPELL_CIRCULAR_BLIZZARD, false); + } + break; + } + + SuperCastTimer = 35000 + (rand()%5000); + }else SuperCastTimer -= diff; + + if(!ElementalsSpawned && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 40) + { + ElementalsSpawned = true; + + for (uint32 i = 0; i < 4; i++) + { + Creature* pUnit = DoSpawnCreature(CREATURE_WATER_ELEMENTAL, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 90000); + if (pUnit) + { + pUnit->Attack(m_creature->getVictim(), true); + pUnit->setFaction(m_creature->getFaction()); + } + } + + DoYell(SAY_ELEMENTALS, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ELEMENTALS); + } + + if(BerserkTimer < diff) + { + for (uint32 i = 0; i < 5; i++) + { + Creature* pUnit = DoSpawnCreature(CREATURE_SHADOW_OF_ARAN, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + if (pUnit) + { + pUnit->Attack(m_creature->getVictim(), true); + pUnit->setFaction(m_creature->getFaction()); + } + } + + DoYell(SAY_TIMEOVER, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_TIMEOVER); + + BerserkTimer = 60000; + }else BerserkTimer -= diff; + + //Flame Wreath check + if (FlameWreathTimer) + { + if (FlameWreathTimer >= diff) + FlameWreathTimer -= diff; + else FlameWreathTimer = 0; + + if (FlameWreathCheckTime < diff) + { + for (uint32 i = 0; i < 3; i++) + { + if (!FlameWreathTarget[i]) + continue; + + Unit* pUnit = Unit::GetUnit(*m_creature, FlameWreathTarget[i]); + if (pUnit && pUnit->GetDistance2d(FWTargPosX[i], FWTargPosY[i]) > 3) + { + pUnit->CastSpell(pUnit, 20476, true, 0, 0, m_creature->GetGUID()); + pUnit->CastSpell(pUnit, 11027, true); + FlameWreathTarget[i] = 0; + } + } + FlameWreathCheckTime = 500; + }else FlameWreathCheckTime -= diff; + } + + if (ArcaneCooldown && FireCooldown && FrostCooldown) + DoMeleeAttackIfReady(); + } + + void DamageTaken(Unit* pAttacker, uint32 &damage) + { + if (!DrinkInturrupted && Drinking && damage) + DrinkInturrupted = true; + } + + void SpellHit(Unit* pAttacker, const SpellEntry* Spell) + { + //We only care about inturrupt effects and only if they are durring a spell currently being casted + if ((Spell->Effect[0]!=SPELL_EFFECT_INTERRUPT_CAST && + Spell->Effect[1]!=SPELL_EFFECT_INTERRUPT_CAST && + Spell->Effect[2]!=SPELL_EFFECT_INTERRUPT_CAST) || !m_creature->IsNonMeleeSpellCasted(false)) + return; + + //Inturrupt effect + m_creature->InterruptNonMeleeSpells(false); + + //Normally we would set the cooldown equal to the spell duration + //but we do not have access to the DurationStore + + switch (CurrentNormalSpell) + { + case SPELL_ARCMISSLE: + ArcaneCooldown = 5000; + break; + case SPELL_FIREBALL: + FireCooldown = 5000; + break; + case SPELL_FROSTBOLT: + FrostCooldown = 5000; + break; + } + } +}; + +struct MANGOS_DLL_DECL water_elementalAI : public ScriptedAI +{ + water_elementalAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 CastTimer; + + void Reset() + { + CastTimer = 2000 + (rand()%3000); + } + + void Aggro(Unit* who) {} + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(CastTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_WATERBOLT); + CastTimer = 2000 + (rand()%3000); + }else CastTimer -= diff; + } +}; + +CreatureAI* GetAI_boss_aran(Creature *_Creature) +{ + return new boss_aranAI (_Creature); +} + +CreatureAI* GetAI_water_elemental(Creature *_Creature) +{ + return new water_elementalAI (_Creature); +} + +// CONVERT TO ACID +CreatureAI* GetAI_shadow_of_aran(Creature *_Creature) +{ + outstring_log("SD2: Convert simpleAI script for Creature Entry %u to ACID", _Creature->GetEntry()); + SimpleAI* ai = new SimpleAI (_Creature); + + ai->Spell[0].Enabled = true; + ai->Spell[0].Spell_Id = SPELL_SHADOW_PYRO; + ai->Spell[0].Cooldown = 5000; + ai->Spell[0].First_Cast = 1000; + ai->Spell[0].Cast_Target_Type = CAST_HOSTILE_TARGET; + + ai->EnterEvadeMode(); + + return ai; +} + +void AddSC_boss_shade_of_aran() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_shade_of_aran"; + newscript->GetAI = GetAI_boss_aran; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_shadow_of_aran"; + newscript->GetAI = GetAI_shadow_of_aran; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_aran_elemental"; + newscript->GetAI = GetAI_water_elemental; + m_scripts[nrscripts++] = newscript; + + //newscript = new Script; + //newscript->Name="mob_aran_blizzard"; + //newscript->GetAI = GetAI_boss_aran; + //m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_terestian_illhoof.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_terestian_illhoof.cpp new file mode 100644 index 00000000000..2fa5b3860a0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_terestian_illhoof.cpp @@ -0,0 +1,454 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Terestian_Illhoof +SD%Complete: 100 +SDComment: Complete! +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "def_karazhan.h" + +#define SPELL_SUMMON_DEMONCHAINS 30120 // Summons demonic chains that maintain the ritual of sacrifice. +#define SPELL_DEMON_CHAINS 30206 // Instant - Visual Effect +#define SPELL_ENRAGE 23537 // Increases the caster's attack speed by 50% and the Physical damage it deals by 219 to 281 for 10 min. +#define SPELL_SHADOW_BOLT 30055 // Hurls a bolt of dark magic at an enemy, inflicting Shadow damage. +#define SPELL_SACRIFICE 30115 // Teleports and adds the debuff +#define SPELL_BERSERK 32965 // Increases attack speed by 75%. Periodically casts Shadow Bolt Volley. + +#define SPELL_FIREBOLT 18086 // Blasts a target for 150 Fire damage. + +#define SPELL_BROKEN_PACT 30065 // All damage taken increased by 25%. +#define SPELL_AMPLIFY_FLAMES 30053 // Increases the Fire damage taken by an enemy by 500 for 25 sec. +#define SPELL_FIREBOLT 18086 // Blasts a target for 150 Fire damage. + +#define SAY_SLAY1 "Your blood will anoint my circle." +#define SOUND_SLAY1 9264 +#define SAY_SLAY2 "The great one will be pleased." +#define SOUND_SLAY2 9329 + +#define SAY_DEATH "My life, is yours. Oh great one." +#define SOUND_DEATH 9262 + +#define SAY_AGGRO "Ah, you're just in time. The rituals are about to begin." +#define SOUND_AGGRO 9260 + +#define SAY_SACRIFICE1 "Please, accept this humble offering, oh great one." +#define SOUND_SACRIFICE1 9263 +#define SAY_SACRIFICE2 "Let the sacrifice serve his testament to my fealty." +#define SOUND_SACRIFICE2 9330 + +#define SAY_SUMMON1 "Come, you dwellers in the dark. Rally to my call!" +#define SOUND_SUMMON1 9265 +#define SAY_SUMMON2 "Gather, my pets. There is plenty for all." +#define SOUND_SUMMON2 9331 + +#define CREATURE_DEMONCHAINS 17248 +#define CREATURE_FIENDISHIMP 17267 +#define CREATURE_PORTAL 17265 + +#define SACRIFICE_X -11240.599 +#define SACRIFICE_Y -1694.2700 +#define SACRIFICE_Z 179.720007 + +#define PORTAL_Z 179.434 + +float PortalLocations[2][2]= +{ + {-11249.6933, -1704.61023}, + {-11242.1160, -1713.33325}, +}; + +struct MANGOS_DLL_DECL mob_kilrekAI : public ScriptedAI +{ + mob_kilrekAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 TerestianGUID; + + uint32 AmplifyTimer; + + void Reset() + { + TerestianGUID = 0; + + AmplifyTimer = 0; + } + + void Aggro(Unit *who) + { + if(!pInstance) + { + ERROR_INST_DATA(m_creature); + return; + } + + Creature* Terestian = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_TERESTIAN))); + if(Terestian && (!Terestian->SelectHostilTarget() && !Terestian->getVictim())) + Terestian->AddThreat(who, 1.0f); + } + + void JustDied(Unit* Killer) + { + if(pInstance) + { + uint64 TerestianGUID = pInstance->GetData64(DATA_TERESTIAN); + if(TerestianGUID) + { + Unit* Terestian = Unit::GetUnit((*m_creature), TerestianGUID); + if(Terestian && Terestian->isAlive()) + DoCast(Terestian, SPELL_BROKEN_PACT, true); + } + }else ERROR_INST_DATA(m_creature); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if (AmplifyTimer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature->getVictim(),SPELL_AMPLIFY_FLAMES); + + AmplifyTimer = 20000; + }else AmplifyTimer -= diff; + + //Chain cast + if (!m_creature->IsNonMeleeSpellCasted(false) && m_creature->IsWithinDistInMap(m_creature->getVictim(), 30)) + DoCast(m_creature->getVictim(),SPELL_FIREBOLT); + else DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_demon_chainAI : public ScriptedAI +{ + mob_demon_chainAI(Creature *c) : ScriptedAI(c) + { + Reset(); + } + + uint64 SacrificeGUID; + + void Reset() + { + SacrificeGUID = 0; + } + + void Aggro(Unit* who) {} + void AttackStart(Unit* who) {} + void MoveInLineOfSight(Unit* who) {} + + void JustDied(Unit *killer) + { + if(SacrificeGUID) + { + Unit* Sacrifice = Unit::GetUnit((*m_creature),SacrificeGUID); + if(Sacrifice) + Sacrifice->RemoveAurasDueToSpell(SPELL_SACRIFICE); + } + } +}; + +struct MANGOS_DLL_DECL boss_terestianAI : public ScriptedAI +{ + boss_terestianAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint64 KilrekGUID; + uint64 PortalGUID[2]; + + uint32 CheckKilrekTimer; + uint32 SacrificeTimer; + uint32 ShadowboltTimer; + uint32 SummonTimer; + uint32 BerserkTimer; + + bool SummonKilrek; + bool SummonedPortals; + bool Berserk; + + void Reset() + { + for(uint8 i = 0; i < 2; ++i) + { + if(PortalGUID[i]) + { + Unit* Portal = Unit::GetUnit((*m_creature), PortalGUID[i]); + if(Portal) + Portal->DealDamage(Portal, Portal->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + PortalGUID[i] = 0; + } + } + + CheckKilrekTimer = 5000; + SacrificeTimer = 30000; + ShadowboltTimer = 5000; + SummonTimer = 10000; + BerserkTimer = 600000; + + SummonedPortals = false; + Berserk = false; + + if(pInstance) + pInstance->SetData(DATA_TERESTIAN_EVENT, NOT_STARTED); + } + + void Aggro(Unit* who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + + if(pInstance) + { + // Put Kil'rek in combat against our target so players don't skip him + Creature* Kilrek = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_KILREK))); + if(Kilrek && (!Kilrek->SelectHostilTarget() && !Kilrek->getVictim())) + Kilrek->AddThreat(who, 1.0f); + + pInstance->SetData(DATA_TERESTIAN_EVENT, IN_PROGRESS); + }else ERROR_INST_DATA(m_creature); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit *killer) + { + for(uint8 i = 0; i < 2; ++i) + { + if(PortalGUID[i]) + { + Unit* Portal = Unit::GetUnit((*m_creature), PortalGUID[i]); + if(Portal) + Portal->DealDamage(Portal, Portal->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + PortalGUID[i] = 0; + } + } + + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if(pInstance) + pInstance->SetData(DATA_TERESTIAN_EVENT, DONE); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->getVictim() && !m_creature->SelectHostilTarget()) + return; + + if(CheckKilrekTimer < diff) + { + CheckKilrekTimer = 5000; + + if(pInstance) + uint64 KilrekGUID = pInstance->GetData64(DATA_KILREK); + else ERROR_INST_DATA(m_creature); + + Creature* Kilrek = ((Creature*)Unit::GetUnit((*m_creature), KilrekGUID)); + if(SummonKilrek && Kilrek) + { + Kilrek->Respawn(); + Kilrek->AI()->AttackStart(m_creature->getVictim()); + + SummonKilrek = false; + } + + if(!Kilrek || !Kilrek->isAlive()) + { + SummonKilrek = true; + CheckKilrekTimer = 45000; + } + }else CheckKilrekTimer -= diff; + + if(SacrificeTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(target && target->isAlive() && target->GetTypeId() == TYPEID_PLAYER) + { + DoCast(target, SPELL_SACRIFICE, true); + Creature* Chains = m_creature->SummonCreature(CREATURE_DEMONCHAINS, SACRIFICE_X, SACRIFICE_Y, SACRIFICE_Z, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 21000); + if(Chains) + { + ((mob_demon_chainAI*)Chains->AI())->SacrificeGUID = target->GetGUID(); + Chains->CastSpell(Chains, SPELL_DEMON_CHAINS, true); + switch(rand()%2) + { + case 0: + DoYell(SAY_SACRIFICE1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SACRIFICE1); + break; + case 1: + DoYell(SAY_SACRIFICE2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SACRIFICE2); + break; + } + SacrificeTimer = 30000; + } + } + }else SacrificeTimer -= diff; + + if(ShadowboltTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_TOPAGGRO, 0), SPELL_SHADOW_BOLT); + ShadowboltTimer = 10000; + }else ShadowboltTimer -= diff; + + if(SummonTimer < diff) + { + if(!SummonedPortals) + { + for(uint8 i = 0; i < 2; ++i) + { + Creature* Portal = m_creature->SummonCreature(CREATURE_PORTAL, PortalLocations[i][0], PortalLocations[i][1], PORTAL_Z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + if(Portal) + PortalGUID[i] = Portal->GetGUID(); + } + SummonedPortals = true; + switch(rand()%2) + { + case 0: + DoYell(SAY_SUMMON1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SUMMON1); + break; + case 1: + DoYell(SAY_SUMMON2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SUMMON2); + break; + } + } + uint32 random = rand()%2; + Creature* Imp = m_creature->SummonCreature(CREATURE_FIENDISHIMP, PortalLocations[random][0], PortalLocations[random][1], PORTAL_Z, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 15000); + if(Imp) + { + Imp->AddThreat(m_creature->getVictim(), 1.0f); + Imp->AI()->AttackStart(SelectUnit(SELECT_TARGET_RANDOM, 1)); + } + SummonTimer = 5000; + }else SummonTimer -= diff; + + if(!Berserk) + if(BerserkTimer < diff) + { + DoCast(m_creature, SPELL_BERSERK); + Berserk = true; + }else BerserkTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_karazhan_impAI : public ScriptedAI +{ + mob_karazhan_impAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FireboltTimer; + + void Reset() + { + FireboltTimer = 3000; + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(FireboltTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FIREBOLT); + FireboltTimer = 1500; + }else FireboltTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_kilrek(Creature *_Creature) +{ + return new mob_kilrekAI (_Creature); +} + +CreatureAI* GetAI_mob_karazhan_imp(Creature *_Creature) +{ + return new mob_karazhan_impAI (_Creature); +} + +CreatureAI* GetAI_mob_demon_chain(Creature *_Creature) +{ + return new mob_demon_chainAI(_Creature); +} + +CreatureAI* GetAI_boss_terestian_illhoof(Creature *_Creature) +{ + return new boss_terestianAI (_Creature); +} + +void AddSC_boss_terestian_illhoof() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_terestian_illhoof"; + newscript->GetAI = GetAI_boss_terestian_illhoof; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_karazhan_imp"; + newscript->GetAI = GetAI_mob_karazhan_imp; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_kilrek"; + newscript->GetAI = GetAI_mob_kilrek; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_demon_chain"; + newscript->GetAI = GetAI_mob_demon_chain; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/karazhan/bosses_opera.cpp b/src/bindings/scripts/scripts/zone/karazhan/bosses_opera.cpp new file mode 100644 index 00000000000..3caedb2dafa --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/bosses_opera.cpp @@ -0,0 +1,1471 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Bosses_Opera +SD%Complete: 90 +SDComment: Oz, Hood, and RAJ event implemented. RAJ event requires more testing. +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "def_karazhan.h" + +/***********************************/ +/*** OPERA WIZARD OF OZ EVENT *****/ +/*********************************/ + +/**** Spells ****/ +// Dorothee +#define SPELL_WATERBOLT 31012 +#define SPELL_SCREAM 31013 +#define SPELL_SUMMONTITO 31014 + +// Tito +#define SPELL_YIPPING 31015 + +// Strawman +#define SPELL_BRAIN_BASH 31046 +#define SPELL_BRAIN_WIPE 31069 +#define SPELL_BURNING_STRAW 31075 + +// Tinhead +#define SPELL_CLEAVE 31043 +#define SPELL_RUST 31086 + +// Roar +#define SPELL_MANGLE 31041 +#define SPELL_SHRED 31042 +#define SPELL_FRIGHTENED_SCREAM 31013 + +// Crone +#define SPELL_CHAIN_LIGHTNING 32337 + +// Cyclone +#define SPELL_KNOCKBACK 32334 +#define SPELL_CYCLONE_VISUAL 32332 + +/** Creature Entries **/ +#define CREATURE_TITO 17548 +#define CREATURE_CYCLONE 18412 +#define CREATURE_CRONE 18168 + +/***** Speech and Sound *****/ +#define SAY_DOROTHEE_DEATH "Oh at last, at last. I can go home." +#define SOUND_DOROTHEE_DEATH 9190 +#define SAY_DOROTHEE_SUMMON "Don't let them hurt us, Tito! Oh, you won't, will you?" +#define SOUND_DOROTHEE_SUMMON 9191 +#define SAY_DOROTHEE_TITO_DEATH "Tito, oh Tito, no!" +#define SOUND_DOROTHEE_TITO_DEATH 9192 +#define SAY_DOROTHEE_AGGRO "Oh dear, we simply must find a way home! The old wizard could be our only hope! Strawman, Roar, Tinhead, will you... wait! Oh golly, look! We have visitors!" +#define SOUND_DOROTHEE_AGGRO 9195 + +#define SAY_ROAR_AGGRO "Wanna fight? Huh? Do ya? C'mon, I'll fight you with both claws behind my back!" +#define SOUND_ROAR_AGGRO 9227 +#define SAY_ROAR_DEATH "You didn't have to go and do that." +#define SOUND_ROAR_DEATH 9229 +#define SAY_ROAR_SLAY "I think I'm going to go take fourty winks" +#define SOUND_ROAR_SLAY 9230 + +#define SAY_STRAWMAN_AGGRO "Now what should I do with you? I simply can't make up my mind." +#define SOUND_STRAWMAN_AGGRO 9254 +#define SAY_STRAWMAN_DEATH "Don't let them make a mattress... out of me." +#define SOUND_STRAWMAN_DEATH 9256 +#define SAY_STRAWMAN_SLAY "I guess I'm not a failure after all." +#define SOUND_STRAWMAN_SLAY 9257 + +#define SAY_TINHEAD_AGGRO "I could really use a heart. Say, can I have yours?" +#define SOUND_TINHEAD_AGGRO 9268 +#define SAY_TINHEAD_DEATH "Back to being an old rustbucket." +#define SOUND_TINHEAD_DEATH 9270 +#define SAY_TINHEAD_SLAY "Guess I'm not so rusty, after all." +#define SOUND_TINHEAD_SLAY 9271 + +#define SAY_CRONE_AGGRO "Woe to each and every one of you my pretties!" +#define SOUND_CRONE_AGGRO 9179 +#define SAY_CRONE_AGGRO2 "It will all be over soon!" +#define SOUND_CRONE_AGGRO2 9307 +#define SAY_CRONE_DEATH "How could you? What a cruel, cruel world!" +#define SOUND_CRONE_DEATH 9178 +#define SAY_CRONE_SLAY "Fixed you, didn't I?" +#define SOUND_CRONE_SLAY 9180 + +void SummonCroneIfReady(ScriptedInstance* pInstance, Creature *_Creature) +{ + pInstance->SetData(DATA_OPERA_OZ_DEATHCOUNT, 0); // Increment DeathCount + if(pInstance->GetData(DATA_OPERA_OZ_DEATHCOUNT) == 4) + { + Creature* Crone = _Creature->SummonCreature(CREATURE_CRONE, -10891.96, -1755.95, _Creature->GetPositionZ(), 4.64, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000); + if(Crone) + { + if(_Creature->getVictim()) + Crone->AI()->AttackStart(_Creature->getVictim()); + + Crone->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + } +}; + +struct MANGOS_DLL_DECL boss_dorotheeAI : public ScriptedAI +{ + boss_dorotheeAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 AggroTimer; + + uint32 WaterBoltTimer; + uint32 FearTimer; + uint32 SummonTitoTimer; + + bool SummonedTito; + bool TitoDied; + bool InCombat; + + void Reset() + { + AggroTimer = 500; + + WaterBoltTimer = 5000; + FearTimer = 15000; + SummonTitoTimer = 47500; + + SummonedTito = false; + TitoDied = false; + InCombat = false; + } + + void Aggro(Unit* who) + { + DoYell(SAY_DOROTHEE_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DOROTHEE_AGGRO); + } + + void SummonTito(); // See below + + void JustDied(Unit* killer) + { + DoYell(SAY_DOROTHEE_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DOROTHEE_DEATH); + + if(pInstance) + SummonCroneIfReady(pInstance, m_creature); + } + + void AttackStart(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::AttackStart(who); + } + + void MoveInLineOfSight(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(who); + } + + void UpdateAI(const uint32 diff) + { + if(AggroTimer) + if(AggroTimer <= diff) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + AggroTimer = 0; + }else AggroTimer -= diff; + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(WaterBoltTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_WATERBOLT); + WaterBoltTimer = TitoDied ? 1500 : 5000; + }else WaterBoltTimer -= diff; + + if(FearTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SCREAM); + FearTimer = 30000; + }else FearTimer -= diff; + + if(!SummonedTito) + { + if(SummonTitoTimer < diff) + SummonTito(); + else SummonTitoTimer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_titoAI : public ScriptedAI +{ + mob_titoAI(Creature* c) : ScriptedAI(c) + { + Reset(); + } + + uint64 DorotheeGUID; + + uint32 YipTimer; + + void Reset() + { + DorotheeGUID = 0; + + YipTimer = 10000; + } + + void Aggro(Unit* who) {} + + void JustDied(Unit* killer) + { + if(DorotheeGUID) + { + Creature* Dorothee = ((Creature*)Unit::GetUnit((*m_creature), DorotheeGUID)); + if(Dorothee && Dorothee->isAlive()) + { + ((boss_dorotheeAI*)Dorothee->AI())->TitoDied = true; + Dorothee->MonsterYell(SAY_DOROTHEE_TITO_DEATH, LANG_UNIVERSAL, 0); + DoPlaySoundToSet(Dorothee, SOUND_DOROTHEE_TITO_DEATH); + } + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(YipTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_YIPPING); + YipTimer = 10000; + }else YipTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +void boss_dorotheeAI::SummonTito() +{ + Creature* Tito = DoSpawnCreature(CREATURE_TITO, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000); + if(Tito) + { + DoYell(SAY_DOROTHEE_SUMMON, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DOROTHEE_SUMMON); + ((mob_titoAI*)Tito->AI())->DorotheeGUID = m_creature->GetGUID(); + Tito->AI()->AttackStart(m_creature->getVictim()); + SummonedTito = true; + TitoDied = false; + } +} + +struct MANGOS_DLL_DECL boss_strawmanAI : public ScriptedAI +{ + boss_strawmanAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 AggroTimer; + uint32 BrainBashTimer; + uint32 BrainWipeTimer; + + void Reset() + { + AggroTimer = 13000; + BrainBashTimer = 5000; + BrainWipeTimer = 7000; + } + + void AttackStart(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::AttackStart(who); + } + + void MoveInLineOfSight(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(who); + } + + void Aggro(Unit* who) + { + DoYell(SAY_STRAWMAN_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_STRAWMAN_AGGRO); + } + + void SpellHit(Unit* caster, const SpellEntry *Spell) + { + if((Spell->SchoolMask == SPELL_SCHOOL_MASK_FIRE) && (!(rand()%10))) + DoCast(m_creature, SPELL_BURNING_STRAW, true); + } + + void JustDied(Unit* killer) + { + DoYell(SAY_STRAWMAN_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_STRAWMAN_DEATH); + + if(pInstance) + SummonCroneIfReady(pInstance, m_creature); + } + + void KilledUnit(Unit* victim) + { + DoYell(SAY_STRAWMAN_SLAY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_STRAWMAN_SLAY); + } + + void UpdateAI(const uint32 diff) + { + if(AggroTimer) + if(AggroTimer <= diff) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + AggroTimer = 0; + }else AggroTimer -= diff; + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(BrainBashTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_BRAIN_BASH); + BrainBashTimer = 15000; + }else BrainBashTimer -= diff; + + if(BrainWipeTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_BRAIN_WIPE); + BrainWipeTimer = 20000; + }else BrainWipeTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_tinheadAI : public ScriptedAI +{ + boss_tinheadAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 AggroTimer; + uint32 CleaveTimer; + uint32 RustTimer; + + uint8 RustCount; + + void Reset() + { + AggroTimer = 15000; + CleaveTimer = 5000; + RustTimer = 30000; + + RustCount = 0; + } + + void Aggro(Unit* who) + { + DoYell(SAY_TINHEAD_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_TINHEAD_AGGRO); + } + + void AttackStart(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::AttackStart(who); + } + + void MoveInLineOfSight(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(who); + } + + void JustDied(Unit* killer) + { + DoYell(SAY_TINHEAD_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_TINHEAD_DEATH); + + if(pInstance) + SummonCroneIfReady(pInstance, m_creature); + } + + void KilledUnit(Unit* victim) + { + DoYell(SAY_TINHEAD_SLAY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_TINHEAD_SLAY); + } + + void UpdateAI(const uint32 diff) + { + if(AggroTimer) + if(AggroTimer < diff) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + AggroTimer = 0; + }else AggroTimer -= diff; + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(CleaveTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CLEAVE); + CleaveTimer = 5000; + }else CleaveTimer -= diff; + + if(RustCount < 8) + { + if(RustTimer < diff) + { + RustCount++; + DoTextEmote("begins to rust", NULL); + DoCast(m_creature, SPELL_RUST); + RustTimer = 6000; + }else RustTimer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_roarAI : public ScriptedAI +{ + boss_roarAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 AggroTimer; + uint32 MangleTimer; + uint32 ShredTimer; + uint32 ScreamTimer; + + void Reset() + { + AggroTimer = 20000; + MangleTimer = 5000; + ShredTimer = 10000; + ScreamTimer = 15000; + } + + void MoveInLineOfSight(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(who); + } + + void AttackStart(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::AttackStart(who); + } + + void Aggro(Unit* who) + { + DoYell(SAY_ROAR_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ROAR_AGGRO); + } + + void JustDied(Unit* killer) + { + DoYell(SAY_ROAR_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ROAR_DEATH); + + if(pInstance) + SummonCroneIfReady(pInstance, m_creature); + } + + void KilledUnit(Unit* victim) + { + DoYell(SAY_ROAR_SLAY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ROAR_SLAY); + } + + void UpdateAI(const uint32 diff) + { + if(AggroTimer) + if(AggroTimer <= diff) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + AggroTimer = 0; + }else AggroTimer -= diff; + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(MangleTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_MANGLE); + MangleTimer = 5000 + rand()%3000; + }else MangleTimer -= diff; + + if(ShredTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SHRED); + ShredTimer = 10000 + rand()%5000; + }else ShredTimer -= diff; + + if(ScreamTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FRIGHTENED_SCREAM); + ScreamTimer = 20000 + rand()%10000; + }else ScreamTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_croneAI : public ScriptedAI +{ + boss_croneAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 CycloneTimer; + uint32 ChainLightningTimer; + + void Reset() + { + CycloneTimer = 30000; + ChainLightningTimer = 10000; + } + + void Aggro(Unit* who) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_CRONE_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CRONE_AGGRO); + break; + case 1: + DoYell(SAY_CRONE_AGGRO2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CRONE_AGGRO2); + break; + } + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + + void JustDied(Unit* killer) + { + DoYell(SAY_CRONE_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CRONE_DEATH); + + if(pInstance) + { + pInstance->SetData(DATA_OPERA_EVENT, DONE); + GameObject* Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_STAGEDOORRIGHT)); + if(Door) + Door->UseDoorOrButton(); + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + if(CycloneTimer < diff) + { + Creature* Cyclone = DoSpawnCreature(CREATURE_CYCLONE, rand()%10, rand()%10, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 15000); + if(Cyclone) + Cyclone->CastSpell(Cyclone, SPELL_CYCLONE_VISUAL, true); + CycloneTimer = 30000; + }else CycloneTimer -= diff; + + if(ChainLightningTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CHAIN_LIGHTNING); + ChainLightningTimer = 15000; + }else ChainLightningTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_cycloneAI : public ScriptedAI +{ + mob_cycloneAI(Creature* c) : ScriptedAI(c) + { + Reset(); + } + + uint32 MoveTimer; + + void Reset() + { + MoveTimer = 1000; + } + + void Aggro(Unit* who) {} + + void MoveInLineOfSight(Unit* who) + { + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->HasAura(SPELL_KNOCKBACK, 0)) + DoCast(m_creature, SPELL_KNOCKBACK, true); + + if(MoveTimer < diff) + { + float x,y,z; + m_creature->GetPosition(x,y,z); + float PosX, PosY, PosZ; + m_creature->GetRandomPoint(x,y,z,10, PosX, PosY, PosZ); + m_creature->GetMotionMaster()->MovePoint(0, PosX, PosY, PosZ); + MoveTimer = 5000 + rand()%3000; + }else MoveTimer -= diff; + } +}; + +CreatureAI* GetAI_boss_dorothee(Creature* _Creature) +{ + return new boss_dorotheeAI(_Creature); +} + +CreatureAI* GetAI_boss_strawman(Creature* _Creature) +{ + return new boss_strawmanAI(_Creature); +} + +CreatureAI* GetAI_boss_tinhead(Creature* _Creature) +{ + return new boss_tinheadAI(_Creature); +} + +CreatureAI* GetAI_boss_roar(Creature* _Creature) +{ + return new boss_roarAI(_Creature); +} + +CreatureAI* GetAI_boss_crone(Creature* _Creature) +{ + return new boss_croneAI(_Creature); +} + +CreatureAI* GetAI_mob_tito(Creature* _Creature) +{ + return new mob_titoAI(_Creature); +} + +CreatureAI* GetAI_mob_cyclone(Creature* _Creature) +{ + return new mob_cycloneAI(_Creature); +} + +/**************************************/ +/**** Opera Red Riding Hood Event ****/ +/************************************/ + +#define GOSSIP_GRANDMA "What phat lewtz you have grandmother?" + +/**** Spells For The Wolf ****/ +#define SPELL_LITTLE_RED_RIDING_HOOD 30768 +#define SPELL_TERRIFYING_HOWL 30752 +#define SPELL_WIDE_SWIPE 30761 + +/**** Yells for the Wolf ****/ +#define SAY_WOLF_AGGRO "All the better to own you with!" +#define SOUND_WOLF_AGGRO 9276 +#define SOUND_WOLF_DEATH 9275 // No speech +#define SAY_WOLF_SLAY "Mmmm... delicious." +#define SOUND_WOLF_SLAY 9277 +#define SAY_WOLF_HOOD "Run away little girl, run away!" +#define SOUND_WOLF_HOOD 9278 + +/**** The Wolf's Entry ****/ +#define CREATURE_BIG_BAD_WOLF 17521 + +bool GossipHello_npc_grandmother(Player* player, Creature* _Creature) +{ + player->ADD_GOSSIP_ITEM(0, GOSSIP_GRANDMA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(8990, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_grandmother(Player* player, Creature* _Creature, uint32 sender, uint32 action) +{ + if(action == GOSSIP_ACTION_INFO_DEF) + { + _Creature->SetVisibility(VISIBILITY_OFF); + float x,y,z; + _Creature->GetPosition(x,y,z); + Creature* BigBadWolf = _Creature->SummonCreature(CREATURE_BIG_BAD_WOLF, x, y, z, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000); + if(BigBadWolf) + { + BigBadWolf->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + BigBadWolf->AI()->AttackStart(player); + } + + _Creature->setDeathState(JUST_DIED); + } + + return true; +} + +struct MANGOS_DLL_DECL boss_bigbadwolfAI : public ScriptedAI +{ + boss_bigbadwolfAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 ChaseTimer; + uint32 FearTimer; + uint32 SwipeTimer; + + uint64 HoodGUID; + float TempThreat; + + bool IsChasing; + + void Reset() + { + ChaseTimer = 30000; + FearTimer = 25000 + rand()%10000; + SwipeTimer = 5000; + + HoodGUID = 0; + TempThreat = 0; + + IsChasing = false; + } + + void Aggro(Unit* who) + { + DoYell(SAY_WOLF_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_WOLF_AGGRO); + } + + void JustDied(Unit* killer) + { + DoPlaySoundToSet(m_creature, SOUND_WOLF_DEATH); + + if(pInstance) + { + pInstance->SetData(DATA_OPERA_EVENT, DONE); + GameObject* Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_STAGEDOORRIGHT)); + if(Door) + Door->UseDoorOrButton(); + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + + if(ChaseTimer < diff) + { + if(!IsChasing) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target && target->GetTypeId() == TYPEID_PLAYER) + { + DoYell(SAY_WOLF_HOOD, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_WOLF_HOOD); + + DoCast(target, SPELL_LITTLE_RED_RIDING_HOOD, true); + TempThreat = m_creature->getThreatManager().getThreat(target); + if(TempThreat) + m_creature->getThreatManager().modifyThreatPercent(target, -100); + HoodGUID = target->GetGUID(); + m_creature->AddThreat(target, 1000000.0f); + ChaseTimer = 20000; + IsChasing = true; + } + } + else + { + IsChasing = false; + Unit* target = Unit::GetUnit((*m_creature), HoodGUID); + if(target) + { + HoodGUID = 0; + if(m_creature->getThreatManager().getThreat(target)) + m_creature->getThreatManager().modifyThreatPercent(target, -100); + m_creature->AddThreat(target, TempThreat); + TempThreat = 0; + } + + ChaseTimer = 40000; + } + }else ChaseTimer -= diff; + + if(IsChasing) + return; + + if(FearTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_TERRIFYING_HOWL); + FearTimer = 25000 + rand()%35000; + }else FearTimer -= diff; + + if(SwipeTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_WIDE_SWIPE); + SwipeTimer = 25000 + rand()%5000; + }else SwipeTimer -= diff; + + } +}; + +CreatureAI* GetAI_boss_bigbadwolf(Creature* _Creature) +{ + return new boss_bigbadwolfAI(_Creature); +} + +/**********************************************/ +/******** Opera Romeo and Juliet Event *******/ +/********************************************/ + +/***** Spells For Julianne *****/ +#define SPELL_BLINDING_PASSION 30890 +#define SPELL_DEVOTION 30887 +#define SPELL_ETERNAL_AFFECTION 30878 +#define SPELL_POWERFUL_ATTRACTION 30889 +#define SPELL_DRINK_POISON 30907 + +/***** Spells For Romulo ****/ +#define SPELL_BACKWARD_LUNGE 30815 +#define SPELL_DARING 30841 +#define SPELL_DEADLY_SWATHE 30817 +#define SPELL_POISON_THRUST 30822 + +/**** Other Misc. Spells ****/ +#define SPELL_UNDYING_LOVE 30951 +#define SPELL_RES_VISUAL 24171 + +/**** Speech and Text *****/ +/****** Julianne *******/ +#define SAY_JULIANNE_AGGRO "What devil art thou, that dost torment me thus?" +#define SOUND_JULIANNE_AGGRO 9196 +#define SAY_JULIANNE_ENTER "Where is my lord? Where is my Romulo?" +#define SOUND_JULIANNE_ENTER 9199 +#define SAY_JULIANNE_DEATH01 "Romulo, I come! Oh... this do I drink to thee!" +#define SOUND_JULIANNE_DEATH01 9198 +#define SAY_JULIANNE_DEATH02 "Where is my Lord? Where is my Romulo? Ohh, happy dagger! This is thy sheath! There rust, and let me die!" +#define SOUND_JULIANNE_DEATH02 9310 +#define SAY_JULIANNE_RESURRECT "Come, gentle night; and give me back my Romulo!" +#define SOUND_JULIANNE_RESURRECT 9200 +#define SAY_JULIANNE_SLAY "Parting is such sweet sorrow." +#define SOUND_JULIANNE_SLAY 9201 + +/****** Romulo *******/ +#define SAY_ROMULO_AGGRO "Wilt thou provoke me? Then have at thee, boy!" +#define SOUND_ROMULO_AGGRO 9233 +#define SAY_ROMULO_DEATH "Thou smilest... upon the stroke that... murders me." +#define SOUND_ROMULO_DEATH 9235 +#define SAY_ROMULO_ENTER "This day's black fate on more days doth depend. This but begins the woe. Others must end." +#define SOUND_ROMULO_ENTER 9236 +#define SAY_ROMULO_RESURRECT "Thou detestable maw, thou womb of death; I enforce thy rotten jaws to open!" +#define SOUND_ROMULO_RESURRECT 9237 +#define SAY_ROMULO_SLAY "How well my comfort is revived by this!" +#define SOUND_ROMULO_SLAY 9238 + +/*** Misc. Information ****/ +#define CREATURE_ROMULO 17533 +#define ROMULO_X -10900 +#define ROMULO_Y -1758 + +enum RAJPhase +{ + PHASE_JULIANNE = 0, + PHASE_ROMULO = 1, + PHASE_BOTH = 2, +}; + +void PretendToDie(Creature* _Creature) +{ + _Creature->InterruptNonMeleeSpells(false); + _Creature->SetHealth(0); + _Creature->StopMoving(); + _Creature->ClearComboPointHolders(); + _Creature->RemoveAllAurasOnDeath(); + _Creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + _Creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + _Creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + _Creature->ClearAllReactives(); + _Creature->SetUInt64Value(UNIT_FIELD_TARGET,0); + _Creature->GetMotionMaster()->Clear(); + _Creature->GetMotionMaster()->MoveIdle(); + _Creature->SetUInt32Value(UNIT_FIELD_BYTES_1,PLAYER_STATE_DEAD); +}; + +void Resurrect(Creature* target) +{ + target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + target->SetHealth(target->GetMaxHealth()); + target->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + target->CastSpell(target, SPELL_RES_VISUAL, true); + if(target->getVictim()) + { + target->SetUInt64Value(UNIT_FIELD_TARGET, target->getVictim()->GetGUID()); + target->GetMotionMaster()->MoveChase(target->getVictim()); + target->AI()->AttackStart(target->getVictim()); + } +}; + +struct MANGOS_DLL_DECL boss_julianneAI : public ScriptedAI +{ + boss_julianneAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + EntryYellTimer = 1000; + AggroYellTimer = 10000; + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 RomuloGUID; + + uint32 Phase; + + uint32 EntryYellTimer; + uint32 AggroYellTimer; + uint32 BlindingPassionTimer; + uint32 DevotionTimer; + uint32 EternalAffectionTimer; + uint32 PowerfulAttractionTimer; + uint32 SummonRomuloTimer; + uint32 ResurrectTimer; + + bool IsFakingDeath; + bool SummonedRomulo; + bool RomuloDead; + + void Reset() + { + if(RomuloGUID) + { + if(Unit* Romulo = Unit::GetUnit(*m_creature, RomuloGUID)) + { + Romulo->SetVisibility(VISIBILITY_OFF); + Romulo->DealDamage(Romulo, Romulo->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + + RomuloGUID = 0; + } + + Phase = PHASE_JULIANNE; + + BlindingPassionTimer = 30000; + DevotionTimer = 15000; + EternalAffectionTimer = 25000; + PowerfulAttractionTimer = 5000; + + if(IsFakingDeath) + Resurrect(m_creature); + + IsFakingDeath = false; + SummonedRomulo = false; + RomuloDead = false; + } + + void Aggro(Unit* who) {} + + void AttackStart(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::AttackStart(who); + } + + void MoveInLineOfSight(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(who); + } + + void DamageTaken(Unit* done_by, uint32 &damage); + + void JustDied(Unit* killer) + { + DoYell(SAY_JULIANNE_DEATH02, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_JULIANNE_DEATH02); + + if(pInstance) + { + pInstance->SetData(DATA_OPERA_EVENT, DONE); + GameObject* Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_STAGEDOORRIGHT)); + if(Door) + Door->UseDoorOrButton(); + } + } + + void KilledUnit(Unit* victim) + { + DoYell(SAY_JULIANNE_SLAY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_JULIANNE_SLAY); + } + + void UpdateAI(const uint32 diff); +}; + +struct MANGOS_DLL_DECL boss_romuloAI : public ScriptedAI +{ + boss_romuloAI(Creature* c) : ScriptedAI(c) + { + Reset(); + EntryYellTimer = 8000; + AggroYellTimer = 15000; + } + + uint64 JulianneGUID; + + uint32 Phase; + + uint32 EntryYellTimer; + uint32 AggroYellTimer; + uint32 BackwardLungeTimer; + uint32 DaringTimer; + uint32 DeadlySwatheTimer; + uint32 PoisonThrustTimer; + uint32 ResurrectTimer; + + bool JulianneDead; + bool IsFakingDeath; + + void Reset() + { + JulianneGUID = 0; + + Phase = PHASE_ROMULO; + + BackwardLungeTimer = 15000; + DaringTimer = 20000; + DeadlySwatheTimer = 25000; + PoisonThrustTimer = 10000; + + if(IsFakingDeath) + Resurrect(m_creature); + + IsFakingDeath = false; + JulianneDead = false; + } + + void DamageTaken(Unit* done_by, uint32 &damage); + + void Aggro(Unit* who) + { + DoYell(SAY_ROMULO_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ROMULO_AGGRO); + if(JulianneGUID) + { + Creature* Julianne = ((Creature*)Unit::GetUnit((*m_creature), JulianneGUID)); + if(Julianne && Julianne->getVictim()) + { + m_creature->AddThreat(Julianne->getVictim(), 1.0f); + DoStartAttackAndMovement(Julianne->getVictim()); + } + } + } + + void MoveInLineOfSight(Unit* who) + { + if(m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + ScriptedAI::MoveInLineOfSight(who); + } + + void JustDied(Unit* killer) + { + DoYell(SAY_ROMULO_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ROMULO_DEATH); + } + + void KilledUnit(Unit* victim) + { + DoYell(SAY_ROMULO_SLAY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ROMULO_SLAY); + } + + void UpdateAI(const uint32 diff); +}; + +void boss_julianneAI::DamageTaken(Unit* done_by, uint32 &damage) +{ + if(damage < m_creature->GetHealth() || done_by == m_creature || done_by->GetGUID() == RomuloGUID) + return; + + if(Phase == PHASE_JULIANNE) + { + DoYell(SAY_JULIANNE_DEATH01, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_JULIANNE_DEATH01); + m_creature->InterruptNonMeleeSpells(true); + DoCast(m_creature, SPELL_DRINK_POISON); + PretendToDie(m_creature); + Phase = PHASE_ROMULO; + damage = 0; + IsFakingDeath = true; + SummonRomuloTimer = 10000; + return; + } + + if(!IsFakingDeath) + { + Creature* Romulo = ((Creature*)Unit::GetUnit((*m_creature), RomuloGUID)); + if(Romulo && Romulo->isAlive() && !((boss_romuloAI*)Romulo->AI())->IsFakingDeath) + { + ((boss_romuloAI*)Romulo->AI())->ResurrectTimer = 10000; + ((boss_romuloAI*)Romulo->AI())->JulianneDead = true; + } + else + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + if(Romulo) + { + Romulo->DealDamage(Romulo, Romulo->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + Romulo->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + JustDied(done_by); + } + + IsFakingDeath = true; + PretendToDie(m_creature); + damage = 0; + } + else + damage = 0; +} + +void boss_romuloAI::DamageTaken(Unit* done_by, uint32 &damage) +{ + if(damage < m_creature->GetHealth() || done_by == m_creature || done_by->GetGUID() == JulianneGUID) + return; + + if(!IsFakingDeath) + { + IsFakingDeath = true; + PretendToDie(m_creature); + + if(Phase == PHASE_BOTH) + { + Creature* Julianne = ((Creature*)Unit::GetUnit((*m_creature), JulianneGUID)); + if(Julianne && Julianne->isAlive() && !((boss_julianneAI*)Julianne->AI())->IsFakingDeath) + { + ((boss_julianneAI*)Julianne->AI())->ResurrectTimer = 10000; + ((boss_julianneAI*)Julianne->AI())->RomuloDead = true; + } + else + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + if(Julianne) + { + Julianne->DealDamage(Julianne, Julianne->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + Julianne->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + JustDied(done_by); + } + } + else + { + Creature* Julianne = ((Creature*)Unit::GetUnit((*m_creature), JulianneGUID)); + if(Julianne) + { + Resurrect(Julianne); + m_creature->SetHealth(m_creature->GetMaxHealth()); + ((boss_julianneAI*)Julianne->AI())->ResurrectTimer = 4000; + ((boss_julianneAI*)Julianne->AI())->RomuloDead = true; + ((boss_julianneAI*)Julianne->AI())->Phase = PHASE_BOTH; + ((boss_julianneAI*)Julianne->AI())->IsFakingDeath = false; + } + Phase = PHASE_BOTH; + } + + damage = 0; + } + + if(IsFakingDeath) damage = 0; +} + +void boss_julianneAI::UpdateAI(const uint32 diff) +{ + if(EntryYellTimer) + if(EntryYellTimer < diff) + { + DoYell(SAY_JULIANNE_ENTER, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_JULIANNE_ENTER); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + EntryYellTimer = 0; + }else EntryYellTimer -= diff; + + if(AggroYellTimer) + if(AggroYellTimer < diff) + { + DoYell(SAY_JULIANNE_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_JULIANNE_AGGRO); + AggroYellTimer = 0; + }else AggroYellTimer -= diff; + + if(Phase == PHASE_ROMULO && !SummonedRomulo) + { + if(SummonRomuloTimer < diff) + { + Creature* Romulo = m_creature->SummonCreature(CREATURE_ROMULO, ROMULO_X, ROMULO_Y, m_creature->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 45000); + if(Romulo) + { + RomuloGUID = Romulo->GetGUID(); + ((boss_romuloAI*)Romulo->AI())->JulianneGUID = m_creature->GetGUID(); + ((boss_romuloAI*)Romulo->AI())->Phase = PHASE_ROMULO; + if(m_creature->getVictim()) + { + Romulo->AI()->AttackStart(m_creature->getVictim()); + Romulo->AddThreat(m_creature->getVictim(), 50.0f); + } + DoZoneInCombat(Romulo); + } + SummonedRomulo = true; + }else SummonRomuloTimer -= diff; + } + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ||IsFakingDeath) + return; + + if(RomuloDead) + if(ResurrectTimer < diff) + { + Creature* Romulo = ((Creature*)Unit::GetUnit((*m_creature), RomuloGUID)); + if(Romulo && ((boss_romuloAI*)Romulo->AI())->IsFakingDeath) + { + DoYell(SAY_JULIANNE_RESURRECT, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_JULIANNE_RESURRECT); + Resurrect(Romulo); + ((boss_romuloAI*)Romulo->AI())->IsFakingDeath = false; + ResurrectTimer = 10000; + } + RomuloDead = false; + }else ResurrectTimer -= diff; + + if(BlindingPassionTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_BLINDING_PASSION); + BlindingPassionTimer = 30000 + rand()%15000; + }else BlindingPassionTimer -= diff; + + if(DevotionTimer < diff) + { + DoCast(m_creature, SPELL_DEVOTION); + DevotionTimer = 15000 + rand()%30000; + }else DevotionTimer -= diff; + + if(PowerfulAttractionTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_POWERFUL_ATTRACTION); + PowerfulAttractionTimer = 5000 + rand()%25000; + }else PowerfulAttractionTimer -= diff; + + if(EternalAffectionTimer < diff) + { + if(rand()%2 == 1 && SummonedRomulo) + { + Creature* Romulo = ((Creature*)Unit::GetUnit((*m_creature), RomuloGUID)); + if(Romulo && Romulo->isAlive() && !((boss_romuloAI*)Romulo->AI())->IsFakingDeath) + DoCast(Romulo, SPELL_ETERNAL_AFFECTION); + else + return; + } + else DoCast(m_creature, SPELL_ETERNAL_AFFECTION); + + EternalAffectionTimer = 45000 + rand()%15000; + }else EternalAffectionTimer -= diff; + + DoMeleeAttackIfReady(); +} + +void boss_romuloAI::UpdateAI(const uint32 diff) +{ + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() || IsFakingDeath) + return; + + if(JulianneDead) + if(ResurrectTimer < diff) + { + Creature* Julianne = ((Creature*)Unit::GetUnit((*m_creature), JulianneGUID)); + if(Julianne && ((boss_julianneAI*)Julianne->AI())->IsFakingDeath) + { + DoYell(SAY_ROMULO_RESURRECT, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ROMULO_RESURRECT); + Resurrect(Julianne); + ((boss_julianneAI*)Julianne->AI())->IsFakingDeath = false; + ResurrectTimer = 10000; + } + JulianneDead = false; + }else ResurrectTimer -= diff; + + if(BackwardLungeTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(target && !m_creature->HasInArc(M_PI, target)) + { + DoCast(target, SPELL_BACKWARD_LUNGE); + BackwardLungeTimer = 15000 + rand()%15000; + } + }else BackwardLungeTimer -= diff; + + if(DaringTimer < diff) + { + DoCast(m_creature, SPELL_DARING); + DaringTimer = 20000 + rand()%20000; + }else DaringTimer -= diff; + + if(DeadlySwatheTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_DEADLY_SWATHE); + DeadlySwatheTimer = 15000 + rand()%10000; + }else DeadlySwatheTimer -= diff; + + if(PoisonThrustTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_POISON_THRUST); + PoisonThrustTimer = 10000 + rand()%10000; + }else PoisonThrustTimer -= diff; + + DoMeleeAttackIfReady(); +} + +CreatureAI* GetAI_boss_julianne(Creature* _Creature) +{ + return new boss_julianneAI(_Creature); +} + +CreatureAI* GetAI_boss_romulo(Creature* _Creature) +{ + return new boss_romuloAI(_Creature); +} + +void AddSC_bosses_opera() +{ + Script* newscript; + + // Oz + newscript = new Script; + newscript->GetAI = GetAI_boss_dorothee; + newscript->Name = "boss_dorothee"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->GetAI = GetAI_boss_strawman; + newscript->Name = "boss_strawman"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->GetAI = GetAI_boss_tinhead; + newscript->Name = "boss_tinhead"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->GetAI = GetAI_boss_roar; + newscript->Name = "boss_roar"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->GetAI = GetAI_boss_crone; + newscript->Name = "boss_crone"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->GetAI = GetAI_mob_tito; + newscript->Name = "mob_tito"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->GetAI = GetAI_mob_cyclone; + newscript->Name = "mob_cyclone"; + m_scripts[nrscripts++] = newscript; + + // Hood + newscript = new Script; + newscript->pGossipHello = GossipHello_npc_grandmother; + newscript->pGossipSelect = GossipSelect_npc_grandmother; + newscript->Name = "npc_grandmother"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->GetAI = GetAI_boss_bigbadwolf; + newscript->Name = "boss_bigbadwolf"; + m_scripts[nrscripts++] = newscript; + + // Romeo And Juliet + newscript = new Script; + newscript->GetAI = GetAI_boss_julianne; + newscript->Name = "boss_julianne"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->GetAI = GetAI_boss_romulo; + newscript->Name = "boss_romulo"; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/karazhan/def_karazhan.h b/src/bindings/scripts/scripts/zone/karazhan/def_karazhan.h new file mode 100644 index 00000000000..938c0131b9c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/def_karazhan.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_KARAZHAN_H +#define DEF_KARAZHAN_H + +#define DATA_ATTUMEN_EVENT 1 +#define DATA_MOROES_EVENT 2 +#define DATA_MAIDENOFVIRTUE_EVENT 3 +#define DATA_OPTIONAL_BOSS_EVENT 4 +#define DATA_OPERA_EVENT 5 +#define DATA_CURATOR_EVENT 6 +#define DATA_SHADEOFARAN_EVENT 7 +#define DATA_TERESTIAN_EVENT 8 +#define DATA_NETHERSPITE_EVENT 9 +#define DATA_CHESS_EVENT 10 +#define DATA_MALCHEZZAR_EVENT 11 +#define DATA_NETHERBANE_EVENT 12 +#define DATA_OPERA_PERFORMANCE 13 +#define DATA_OPERA_OZ_DEATHCOUNT 14 +#define DATA_KILREK 15 +#define DATA_TERESTIAN 16 +#define DATA_MOROES 17 +#define DATA_GAMEOBJECT_CURTAINS 18 +#define DATA_GAMEOBJECT_STAGEDOORLEFT 19 +#define DATA_GAMEOBJECT_STAGEDOORRIGHT 20 +#define DATA_GAMEOBJECT_LIBRARY_DOOR 21 +#define DATA_GAMEOBJECT_MASSIVE_DOOR 22 +#define DATA_GAMEOBJECT_NETHER_DOOR 23 +#define DATA_GAMEOBJECT_GAME_DOOR 24 +#define DATA_GAMEOBJECT_GAME_EXIT_DOOR 25 + +// Opera Performances +#define EVENT_OZ 1 +#define EVENT_HOOD 2 +#define EVENT_RAJ 3 + +#define ERROR_INST_DATA(a) error_log("SD2: Instance Data for Karazhan not set properly. Encounter for Creature Entry %u may not work properly.", a->GetEntry()); +#endif diff --git a/src/bindings/scripts/scripts/zone/karazhan/instance_karazhan.cpp b/src/bindings/scripts/scripts/zone/karazhan/instance_karazhan.cpp new file mode 100644 index 00000000000..f7b2be1978f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/instance_karazhan.cpp @@ -0,0 +1,253 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Karazhan +SD%Complete: 70 +SDComment: Instance Script for Karazhan to help in various encounters. TODO: GameObject visibility for Opera event. +SDCategory: Karazhan +EndScriptData */ + +#include "precompiled.h" +#include "def_karazhan.h" + +#define ENCOUNTERS 11 + +/* +0 - Attumen + Midnight (optional) +1 - Moroes +2 - Maiden of Virtue (optional) +3 - Hyakiss the Lurker / Rokad the Ravager / Shadikith the Glider +4 - Opera Event +5 - Curator +6 - Shade of Aran (optional) +7 - Terestian Illhoof (optional) +8 - Netherspite (optional) +9 - Chess Event +10 - Prince Malchezzar +11 - Netherbane +*/ +struct MANGOS_DLL_DECL instance_karazhan : public ScriptedInstance +{ + instance_karazhan(Map* map) : ScriptedInstance(map) + { + Initialize(); + } + + uint32 Encounters[ENCOUNTERS]; + + uint32 OperaEvent; // 0 - OZ, 1 - HOOD, 2 - RAJ + uint32 OzDeathCount; + + uint64 CurtainGUID; + uint64 StageDoorLeftGUID; + uint64 StageDoorRightGUID; + uint64 KilrekGUID; + uint64 TerestianGUID; + uint64 MoroesGUID; + uint64 LibraryDoor; // Door at Shade of Aran + uint64 MassiveDoor; // Door at Netherspite + uint64 GamesmansDoor; // Door before Chess + uint64 GamesmansExitDoor; // Door after Chess + uint64 NetherspaceDoor; // Door at Malchezaar + + void Initialize() + { + for (uint8 i = 0; i < ENCOUNTERS; ++i) + Encounters[i] = NOT_STARTED; + + OperaEvent = rand()%3; // This never gets altered. + OzDeathCount = 0; + + CurtainGUID = 0; + StageDoorLeftGUID = 0; + StageDoorRightGUID = 0; + + KilrekGUID = 0; + TerestianGUID = 0; + MoroesGUID = 0; + + LibraryDoor = 0; + MassiveDoor = 0; + GamesmansDoor = 0; + GamesmansExitDoor = 0; + NetherspaceDoor = 0; + } + + bool IsEncounterInProgress() const + { + for (uint8 i = 0; i < ENCOUNTERS; ++i) + if (Encounters[i] == IN_PROGRESS) return true; + + return false; + } + + uint32 GetData(uint32 identifier) + { + switch (identifier) + { + case DATA_ATTUMEN_EVENT: return Encounters[0]; + case DATA_MOROES_EVENT: return Encounters[1]; + case DATA_MAIDENOFVIRTUE_EVENT: return Encounters[2]; + case DATA_OPTIONAL_BOSS_EVENT: return Encounters[3]; + case DATA_OPERA_EVENT: return Encounters[4]; + case DATA_CURATOR_EVENT: return Encounters[5]; + case DATA_SHADEOFARAN_EVENT: return Encounters[6]; + case DATA_TERESTIAN_EVENT: return Encounters[7]; + case DATA_NETHERSPITE_EVENT: return Encounters[8]; + case DATA_CHESS_EVENT: return Encounters[9]; + case DATA_MALCHEZZAR_EVENT: return Encounters[10]; + case DATA_NETHERBANE_EVENT: return Encounters[11]; + case DATA_OPERA_PERFORMANCE: return OperaEvent; + case DATA_OPERA_OZ_DEATHCOUNT: return OzDeathCount; + } + + return 0; + } + + void OnCreatureCreate(Creature *creature, uint32 entry) + { + switch (entry) + { + case 17229: KilrekGUID = creature->GetGUID(); break; + case 15688: TerestianGUID = creature->GetGUID(); break; + case 15687: MoroesGUID = creature->GetGUID(); break; + } + } + + uint64 GetData64(uint32 identifier) + { + switch (identifier) + { + case DATA_KILREK: return KilrekGUID; + case DATA_TERESTIAN: return TerestianGUID; + case DATA_MOROES: return MoroesGUID; + case DATA_GAMEOBJECT_STAGEDOORLEFT: return StageDoorLeftGUID; + case DATA_GAMEOBJECT_STAGEDOORRIGHT: return StageDoorRightGUID; + case DATA_GAMEOBJECT_CURTAINS: return CurtainGUID; + case DATA_GAMEOBJECT_LIBRARY_DOOR: return LibraryDoor; + case DATA_GAMEOBJECT_MASSIVE_DOOR: return MassiveDoor; + case DATA_GAMEOBJECT_GAME_DOOR: return GamesmansDoor; + case DATA_GAMEOBJECT_GAME_EXIT_DOOR: return GamesmansExitDoor; + case DATA_GAMEOBJECT_NETHER_DOOR: return NetherspaceDoor; + } + + return 0; + } + + void SetData(uint32 identifier, uint32 data) + { + switch (identifier) + { + case DATA_ATTUMEN_EVENT: Encounters[0] = data; break; + case DATA_MOROES_EVENT: Encounters[1] = data; break; + case DATA_MAIDENOFVIRTUE_EVENT: Encounters[2] = data; break; + case DATA_OPTIONAL_BOSS_EVENT: Encounters[3] = data; break; + case DATA_OPERA_EVENT: Encounters[4] = data; break; + case DATA_CURATOR_EVENT: Encounters[5] = data; break; + case DATA_SHADEOFARAN_EVENT: Encounters[6] = data; break; + case DATA_TERESTIAN_EVENT: Encounters[7] = data; break; + case DATA_NETHERSPITE_EVENT: Encounters[8] = data; break; + case DATA_CHESS_EVENT: Encounters[9] = data; break; + case DATA_MALCHEZZAR_EVENT: Encounters[10] = data; break; + case DATA_NETHERBANE_EVENT: Encounters[11] = data; break; + + case DATA_OPERA_OZ_DEATHCOUNT: ++OzDeathCount; break; + } + + if(data == DONE) + SaveToDB(); + } + + void OnObjectCreate(GameObject* go) + { + switch(go->GetEntry()) + { + case 183932: CurtainGUID = go->GetGUID(); break; + case 184278: StageDoorLeftGUID = go->GetGUID(); break; + case 184279: StageDoorRightGUID = go->GetGUID(); break; + case 184517: LibraryDoor = go->GetGUID(); break; + case 185521: MassiveDoor = go->GetGUID(); break; + case 184276: GamesmansDoor = go->GetGUID(); break; + case 184277: GamesmansExitDoor = go->GetGUID(); break; + case 185134: NetherspaceDoor = go->GetGUID(); break; + } + + switch(OperaEvent) + { + //TODO: Set Object visibilities for Opera based on performance + case EVENT_OZ: + break; + + case EVENT_HOOD: + break; + + case EVENT_RAJ: + break; + } + } + + const char* Save() + { + OUT_SAVE_INST_DATA; + std::ostringstream stream; + stream << Encounters[0] << " " << Encounters[1] << " " << Encounters[2] << " " << Encounters[3] << " " + << Encounters[4] << " " << Encounters[5] << " " << Encounters[6] << " " << Encounters[7] << " " + << Encounters[8] << " " << Encounters[9] << " " << Encounters[10]; + char* out = new char[stream.str().length() + 1]; + strcpy(out, stream.str().c_str()); + if(out) + { + OUT_SAVE_INST_DATA_COMPLETE; + return out; + } + + return NULL; + } + + void Load(const char* in) + { + if(!in) + { + OUT_LOAD_INST_DATA_FAIL; + return; + } + + OUT_LOAD_INST_DATA(in); + std::istringstream stream(in); + stream >> Encounters[0] >> Encounters[1] >> Encounters[2] >> Encounters[3] + >> Encounters[4] >> Encounters[5] >> Encounters[6] >> Encounters[7] + >> Encounters[8] >> Encounters[9] >> Encounters[10]; + for(uint8 i = 0; i < ENCOUNTERS; ++i) + if(Encounters[i] == IN_PROGRESS) // Do not load an encounter as "In Progress" - reset it instead. + Encounters[i] = NOT_STARTED; + OUT_LOAD_INST_DATA_COMPLETE; + } +}; + +InstanceData* GetInstanceData_instance_karazhan(Map* map) +{ + return new instance_karazhan(map); +} + +void AddSC_instance_karazhan() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_karazhan"; + newscript->GetInstanceData = GetInstanceData_instance_karazhan; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/karazhan/karazhan.cpp b/src/bindings/scripts/scripts/zone/karazhan/karazhan.cpp new file mode 100644 index 00000000000..b12c3280f58 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/karazhan/karazhan.cpp @@ -0,0 +1,470 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Karazhan +SD%Complete: 100 +SDComment: Support for Barnes (Opera controller) and Berthold (Doorman). +SDCategory: Karazhan +EndScriptData */ + +/* ContentData +npc_barnes +npc_berthold +EndContentData */ + +#include "precompiled.h" +#include "def_karazhan.h" +#include "../../npc/npc_escortAI.h" + +/*###### +# npc_barnesAI +######*/ + +#define GOSSIP_READY "I'm not an actor." + +#define SAY_READY "Splendid, I'm going to get the audience ready. Break a leg!" +#define SAY_OZ_INTRO1 "Finally, everything is in place. Are you ready for your big stage debut?" +#define OZ_GOSSIP1 "I'm not an actor." +#define SAY_OZ_INTRO2 "Don't worry, you'll be fine. You look like a natural!" +#define OZ_GOSSIP2 "Ok, I'll give it a try, then." + +#define SAY_RAJ_INTRO1 "The romantic plays are really tough, but you'll do better this time. You have TALENT. Ready?" +#define RAJ_GOSSIP1 "I've never been more ready." + +struct Dialogue +{ + char* text; + uint32 soundid, timer; +}; + +static Dialogue OzDialogue[]= +{ + {"Welcome Ladies and Gentlemen, to this evening's presentation!", 9174, 6000}, + {"Tonight we plumb the depths of the human soul as we join a lost, lonely girl trying desperately -- with the help of her loyal companions -- to find her way home!", 9338, 18000}, + {"But she is pursued... by a wicked malevolent crone!", 9339, 9000}, + {"Will she survive? Will she prevail? Only time will tell. And now ... on with the show!", 9340, 15000} +}; + +static Dialogue HoodDialogue[]= +{ + {"Good evening, Ladies and Gentlemen! Welcome to this evening's presentation!", 9175, 6000}, + {"Tonight, things are not what they seem. For tonight, your eyes may not be trusted", 9335, 10000}, + {"Take for instance, this quiet, elderly woman, waiting for a visit from her granddaughter. Surely there is nothing to fear from this sweet, grey-haired, old lady.", 9336, 14000}, + {"But don't let me pull the wool over your eyes. See for yourself what lies beneath those covers! And now... on with the show!", 9337, 15000} +}; + +static Dialogue RAJDialogue[]= +{ + {"Welcome, Ladies and Gentlemen, to this evening's presentation!", 9176, 5000}, + {"Tonight, we explore a tale of forbidden love!", 9341, 7000}, + {"But beware, for not all love stories end happily, as you may find out. Sometimes, love pricks like a thorn.", 9342, 14000}, + {"But don't take it from me, see for yourself what tragedy lies ahead when the paths of star-crossed lovers meet. And now...on with the show!", 9343, 14000} +}; + +// Entries and spawn locations for creatures in Oz event +float Spawns[6][2]= +{ + {17535, -10896}, // Dorothee + {17546, -10891}, // Roar + {17547, -10884}, // Tinhead + {17543, -10902}, // Strawman + {17603, -10892}, // Grandmother + {17534, -10900}, // Julianne +}; + +float StageLocations[6][2]= +{ + {-10866.711, -1779.816}, // Open door, begin walking (0) + {-10894.917, -1775.467}, // (1) + {-10896.044, -1782.619}, // Begin Speech after this (2) + {-10894.917, -1775.467}, // Resume walking (back to spawn point now) after speech (3) + {-10866.711, -1779.816}, // (4) + {-10866.700, -1781.030} // Summon mobs, open curtains, close door (5) +}; + +#define CREATURE_SPOTLIGHT 19525 + +#define SPELL_SPOTLIGHT 25824 +#define SPELL_TUXEDO 32616 + +#define SPAWN_Z 90.5 +#define SPAWN_Y -1758 +#define SPAWN_O 4.738 + +struct MANGOS_DLL_DECL npc_barnesAI : public npc_escortAI +{ + npc_barnesAI(Creature* c) : npc_escortAI(c) + { + RaidWiped = false; + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 SpotlightGUID; + + uint32 TalkCount; + uint32 TalkTimer; + uint32 CurtainTimer; + uint32 WipeTimer; + uint32 Event; + + bool PerformanceReady; + bool RaidWiped; + bool IsTalking; + + void Reset() + { + TalkCount = 0; + TalkTimer = 2000; + CurtainTimer = 5000; + WipeTimer = 5000; + + PerformanceReady = false; + IsTalking = false; + + if(pInstance) + { + pInstance->SetData(DATA_OPERA_EVENT, NOT_STARTED); + + Event = pInstance->GetData(DATA_OPERA_PERFORMANCE); + + GameObject* Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_STAGEDOORLEFT)); + if(Door) + Door->SetGoState(1); + + GameObject* Curtain = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_CURTAINS)); + if(Curtain) + Curtain->SetGoState(1); + } + } + + void Aggro(Unit* who) {} + + void WaypointReached(uint32 i) + { + switch(i) + { + case 2: + IsBeingEscorted = false; + TalkCount = 0; + IsTalking = true; + + float x,y,z; + m_creature->GetPosition(x, y, z); + Creature* Spotlight; + Spotlight = m_creature->SummonCreature(CREATURE_SPOTLIGHT, x, y, z, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 50000); + if(Spotlight) + { + Spotlight->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Spotlight->CastSpell(Spotlight, SPELL_SPOTLIGHT, false); + SpotlightGUID = Spotlight->GetGUID(); + } + break; + + case 5: + if(pInstance) + { + GameObject* Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_STAGEDOORLEFT)); + if(Door) + Door->SetGoState(1); + } + IsBeingEscorted = false; + PerformanceReady = true; + break; + } + } + + void Talk(uint32 count) + { + char* text = NULL; + uint32 sound = 0; + + switch(Event) + { + case EVENT_OZ: + if(OzDialogue[count].text) + text = OzDialogue[count].text; + if(OzDialogue[count].soundid) + sound = OzDialogue[count].soundid; + if(OzDialogue[count].timer) + TalkTimer = OzDialogue[count].timer; + break; + + case EVENT_HOOD: + if(HoodDialogue[count].text) + text = HoodDialogue[count].text; + if(HoodDialogue[count].soundid) + sound = HoodDialogue[count].soundid; + if(HoodDialogue[count].timer) + TalkTimer = HoodDialogue[count].timer; + break; + + case EVENT_RAJ: + if(RAJDialogue[count].text) + text = RAJDialogue[count].text; + if(RAJDialogue[count].soundid) + sound = RAJDialogue[count].soundid; + if(RAJDialogue[count].timer) + TalkTimer = RAJDialogue[count].timer; + break; + } + + if(text) + DoYell(text, LANG_UNIVERSAL, 0); + if(sound) + DoPlaySoundToSet(m_creature, sound); + } + + void UpdateAI(const uint32 diff) + { + npc_escortAI::UpdateAI(diff); + + if(IsTalking) + { + if(TalkTimer < diff) + { + if(TalkCount > 3) + { + Unit* Spotlight = Unit::GetUnit((*m_creature), SpotlightGUID); + if(Spotlight) + { + Spotlight->RemoveAllAuras(); + Spotlight->SetVisibility(VISIBILITY_OFF); + } + + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STAND); + IsTalking = false; + IsBeingEscorted = true; + return; + } + + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_TALK); + Talk(TalkCount); + ++TalkCount; + } + else TalkTimer -= diff; + } + + if(PerformanceReady) + { + if(CurtainTimer) + if(CurtainTimer <= diff) + { + PrepareEncounter(); + + if(!pInstance) + return; + + GameObject* Curtain = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_CURTAINS)); + if(Curtain) + Curtain->SetGoState(0); + + CurtainTimer = 0; + }else CurtainTimer -= diff; + + if(!RaidWiped) + { + if(WipeTimer < diff) + { + Map *map = m_creature->GetMap(); + if(!map->IsDungeon()) return; + + InstanceMap::PlayerList const &PlayerList = ((InstanceMap*)map)->GetPlayers(); + if(PlayerList.empty()) + return; + + RaidWiped = true; + for(InstanceMap::PlayerList::const_iterator i = PlayerList.begin();i != PlayerList.end(); ++i) + { + if((*i)->isAlive() && !(*i)->isGameMaster()) + { + RaidWiped = false; + break; + } + } + + if(RaidWiped) + { + RaidWiped = true; + EnterEvadeMode(); + } + + WipeTimer = 15000; + }else WipeTimer -= diff; + } + + } + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } + + void StartEvent() + { + if(!pInstance) + return; + + pInstance->SetData(DATA_OPERA_EVENT, IN_PROGRESS); + + GameObject* Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GAMEOBJECT_STAGEDOORLEFT)); + if(Door) + Door->SetGoState(0); + + m_creature->CastSpell(m_creature, SPELL_TUXEDO, true); + m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + + Start(false, false, false); + } + + void PrepareEncounter() + { + debug_log("SD2: Barnes Opera Event - Introduction complete - preparing encounter %d", Event); + uint8 index = 0; + uint8 count = 0; + switch(Event) + { + case EVENT_OZ: + index = 0; + count = 4; + break; + + case EVENT_HOOD: + index = 4; + count = index+1; + break; + + case EVENT_RAJ: + index = 5; + count = index+1; + break; + } + + for( ; index < count; ++index) + { + uint32 entry = ((uint32)Spawns[index][0]); + float PosX = Spawns[index][1]; + Creature* pCreature = m_creature->SummonCreature(entry, PosX, SPAWN_Y, SPAWN_Z, SPAWN_O, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000); + if(pCreature) + { + // In case database has bad flags + pCreature->SetUInt32Value(UNIT_FIELD_FLAGS, 0); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + } + + CurtainTimer = 10000; + PerformanceReady = true; + RaidWiped = false; + } +}; + +CreatureAI* GetAI_npc_barnesAI(Creature* _Creature) +{ + npc_barnesAI* Barnes_AI = new npc_barnesAI(_Creature); + + for(uint8 i = 0; i < 6; ++i) + Barnes_AI->AddWaypoint(i, StageLocations[i][0], StageLocations[i][1], 90.465); + + return ((CreatureAI*)Barnes_AI); +} + +bool GossipHello_npc_barnes(Player* player, Creature* _Creature) +{ + // Check for death of Moroes. + ScriptedInstance* pInstance = ((ScriptedInstance*)_Creature->GetInstanceData()); + if(pInstance && (pInstance->GetData(DATA_MOROES_EVENT) >= DONE)) + { + player->ADD_GOSSIP_ITEM(0, OZ_GOSSIP1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + if(!((npc_barnesAI*)_Creature->AI())->RaidWiped) + player->SEND_GOSSIP_MENU(8970, _Creature->GetGUID()); + else + player->SEND_GOSSIP_MENU(8975, _Creature->GetGUID()); + } + else + player->SEND_GOSSIP_MENU(8978, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_barnes(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch(action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM(0, OZ_GOSSIP2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(8971, _Creature->GetGUID()); + break; + + case GOSSIP_ACTION_INFO_DEF+2: + player->CLOSE_GOSSIP_MENU(); + ((npc_barnesAI*)_Creature->AI())->StartEvent(); + break; + } + + return true; +} + +/*### +# npc_berthold +####*/ + +#define SPELL_TELEPORT 39567 + +#define GOSSIP_ITEM_TELEPORT "Teleport me to the Guardian's Library" + +bool GossipHello_npc_berthold(Player* player, Creature* _Creature) +{ + ScriptedInstance* pInstance = ((ScriptedInstance*)_Creature->GetInstanceData()); + // Check if Shade of Aran is dead or not + if(pInstance && (pInstance->GetData(DATA_SHADEOFARAN_EVENT) >= DONE)) + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM_TELEPORT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_berthold(Player* player, Creature* _Creature, uint32 sender, uint32 action) +{ + if(action == GOSSIP_ACTION_INFO_DEF + 1) + player->CastSpell(player, SPELL_TELEPORT, true); + + player->CLOSE_GOSSIP_MENU(); + return true; +} + +void AddSC_karazhan() +{ + Script* newscript; + + newscript = new Script; + newscript->GetAI = GetAI_npc_barnesAI; + newscript->Name = "npc_barnes"; + newscript->pGossipHello = GossipHello_npc_barnes; + newscript->pGossipSelect = GossipSelect_npc_barnes; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "npc_berthold"; + newscript->pGossipHello = GossipHello_npc_berthold; + newscript->pGossipSelect = GossipSelect_npc_berthold; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/loch_modan/loch_modan.cpp b/src/bindings/scripts/scripts/zone/loch_modan/loch_modan.cpp new file mode 100644 index 00000000000..af335dba05f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/loch_modan/loch_modan.cpp @@ -0,0 +1,91 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Loch_Modan +SD%Complete: 100 +SDComment: Quest support: 3181 (only to argue with pebblebitty to get to searing gorge, before quest rewarded) +SDCategory: Loch Modan +EndScriptData */ + +/* ContentData +npc_mountaineer_pebblebitty +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_mountaineer_pebblebitty +######*/ + +bool GossipHello_npc_mountaineer_pebblebitty(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (!player->GetQuestRewardStatus(3181) == 1) + player->ADD_GOSSIP_ITEM( 0, "Open the gate please, i need to get to Searing Gorge", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_mountaineer_pebblebitty(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "But i need to get there, now open the gate!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(1833, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "Ok, so what is this other way?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(1834, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, "Doesn't matter, i'm invulnerable.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->SEND_GOSSIP_MENU(1835, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM( 0, "Yes...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(1836, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->ADD_GOSSIP_ITEM( 0, "Ok, i'll try to remember that.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(1837, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + player->ADD_GOSSIP_ITEM( 0, "A key? Ok!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(1838, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+7: + player->CLOSE_GOSSIP_MENU(); + break; + } + return true; +} + +void AddSC_loch_modan() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_mountaineer_pebblebitty"; + newscript->pGossipHello = &GossipHello_npc_mountaineer_pebblebitty; + newscript->pGossipSelect = &GossipSelect_npc_mountaineer_pebblebitty; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/magisters_terrace/boss_felblood_kaelthas.cpp b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_felblood_kaelthas.cpp new file mode 100644 index 00000000000..5783d1ac441 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_felblood_kaelthas.cpp @@ -0,0 +1,591 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: boss_felblood_kaelthas +SD%Complete: 80 +SDComment: Normal and Heroic Support. Issues: Arcane Spheres do not initially follow targets. TODO: Convert Phoenix to ACID. +SDCategory: Magisters' Terrace +EndScriptData */ + +#include "precompiled.h" +#include "def_magisters_terrace.h" +#include "WorldPacket.h" + +/*** Spells ***/ + +// Phase 1 spells + +#define SPELL_FIREBALL_NORMAL 44189 // Deals 2700-3300 damage at current target +#define SPELL_FIREBALL_HEROIC 46164 // 4950-6050 + +#define SPELL_PHOENIX 44194 // Summons a phoenix (Doesn't work?) +#define SPELL_PHOENIX_BURN 44198 // A spell Phoenix uses to damage everything around + +#define SPELL_FLAMESTRIKE1_NORMAL 44190 // Damage part +#define SPELL_FLAMESTRIKE1_HEROIC 46163 // Heroic damage part +#define SPELL_FLAMESTRIKE2 44191 // Flamestrike indicator before the damage +#define SPELL_FLAMESTRIKE3 44192 // Summons the trigger + animation (projectile) + +#define SPELL_SHOCK_BARRIER 46165 // Heroic only; 10k damage shield, followed by Pyroblast +#define SPELL_PYROBLAST 36819 // Heroic only; 45-55k fire damage + +// Phase 2 spells + +#define SPELL_GRAVITY_LAPSE_INITIAL 44224 // Cast at the beginning of every Gravity Lapse +#define SPELL_GRAVITY_LAPSE_CHANNEL 44251 // Channeled; blue beam animation to every enemy in range +#define SPELL_TELEPORT_CENTER 44218 // Should teleport people to the center. Requires DB entry in spell_target_position. +#define SPELL_GRAVITY_LAPSE_FLY 44227 // Hastens flyspeed and allows flying for 1 minute. For some reason removes 44226. +#define SPELL_GRAVITY_LAPSE_DOT 44226 // Knocks up in the air and applies a 300 DPS DoT. +#define SPELL_ARCANE_SPHERE_PASSIVE 44263 // Passive auras on Arcane Spheres +#define SPELL_POWER_FEEDBACK 44233 // Stuns him, making him take 50% more damage for 10 seconds. Cast after Gravity Lapse + +/*** Creatures ***/ +#define CREATURE_PHOENIX 24674 +#define CREATURE_PHOENIX_EGG 24675 +#define CREATURE_ARCANE_SPHERE 24708 + +/*** Dialogues ***/ +#define SAY_AGGRO "Don't look so smug! I know what you're thinking, but Tempest Keep was merely a set back. Did you honestly believe I would trust the future to some blind, half-night elf mongrel? Oh no, he was merely an instrument, a stepping stone to a much larger plan! It has all led to this, and this time, you will not interfere!" +#define SOUND_AGGRO 12413 // This yell should be done when the room is cleared. For now, set it as aggro yell. + +#define SAY_PHOENIX "Vengeance burns!" +#define SOUND_PHOENIX 12415 + +#define SAY_FLAMESTRIKE "Felomin ashal!" +#define SOUND_FLAMESTRIKE 12417 + +#define SAY_GRAVITY_LAPSE "I'll turn your world... upside... down..." +#define SOUND_GRAVITY_LAPSE 12418 + +#define SAY_TIRED "Master... grant me strength." +#define SOUND_TIRED 12419 + +#define SAY_RECAST_GRAVITY "Do not... get too comfortable." +#define SOUND_RECAST_GRAVITY 12420 + +#define SAY_DEATH "My demise accomplishes nothing! The Master will have you! You will drown in your own blood! This world shall burn! Aaaghh!" +#define SOUND_DEATH 12421 + +/** Locations **/ +float KaelLocations[3][2]= +{ + {148.744659, 181.377426}, + {140.823883, 195.403046}, + {156.574188, 195.650482}, +}; +#define LOCATION_Z -16.727455 + +struct MANGOS_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI +{ + boss_felblood_kaelthasAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + Heroic = c->GetMap()->IsHeroic() ? true : false; + } + + ScriptedInstance* pInstance; + + uint32 FireballTimer; + uint32 PhoenixTimer; + uint32 FlameStrikeTimer; + uint32 CombatPulseTimer; + + //Heroic only + uint32 PyroblastTimer; + + uint32 GravityLapseTimer; + uint32 GravityLapsePhase; + // 0 = No Gravity Lapse + // 1 = Casting Gravity Lapse visual + // 2 = Teleported people to self + // 3 = Knocked people up in the air + // 4 = Applied an aura that allows them to fly, channeling visual, relased Arcane Orbs. + + bool FirstGravityLapse; + bool Heroic; + + uint8 Phase; + // 0 = Not started + // 1 = Fireball; Summon Phoenix; Flamestrike + // 2 = Gravity Lapses + + void Reset() + { + // TODO: Timers + FireballTimer = 0; + PhoenixTimer = 30000; + FlameStrikeTimer = 25000; + CombatPulseTimer = 0; + + PyroblastTimer = 60000; + + GravityLapseTimer = 0; + GravityLapsePhase = 0; + + FirstGravityLapse = true; + + Phase = 0; + + if(pInstance) + pInstance->SetData(DATA_KAELTHAS_EVENT, 0); + } + + void JustDied(Unit *killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + void DamageTaken(Unit* done_by, uint32 &damage) + { + if(damage > m_creature->GetHealth()) + RemoveGravityLapse(); // Remove Gravity Lapse so that players fall to ground if they kill him when in air. + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void SetThreatList(Creature* SummonedUnit) + { + if(!SummonedUnit) return; + + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + std::list::iterator i = m_threatlist.begin(); + for(i = m_threatlist.begin(); i != m_threatlist.end(); i++) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit && pUnit->isAlive()) + { + float threat = m_creature->getThreatManager().getThreat(pUnit); + SummonedUnit->AddThreat(pUnit, threat); + } + } + } + + void TeleportPlayersToSelf() + { + float x = KaelLocations[0][0]; + float y = KaelLocations[0][1]; + m_creature->Relocate(x, y, LOCATION_Z, 0); + //m_creature->SendMonsterMove(x, y, LOCATION_Z, 0, 0, 0); // causes some issues... + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for (i = m_creature->getThreatManager().getThreatList().begin(); i!= m_creature->getThreatManager().getThreatList().end();++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit && (pUnit->GetTypeId() == TYPEID_PLAYER)) + pUnit->CastSpell(pUnit, SPELL_TELEPORT_CENTER, true); + } + DoCast(m_creature, SPELL_TELEPORT_CENTER, true); + } + + void CastGravityLapseKnockUp() + { + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for (i = m_creature->getThreatManager().getThreatList().begin(); i!= m_creature->getThreatManager().getThreatList().end();++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit && (pUnit->GetTypeId() == TYPEID_PLAYER)) + // Knockback into the air + pUnit->CastSpell(pUnit, SPELL_GRAVITY_LAPSE_DOT, true, 0, 0, m_creature->GetGUID()); + } + } + + void CastGravityLapseFly() // Use Fly Packet hack for now as players can't cast "fly" spells unless in map 530. Has to be done a while after they get knocked into the air... + { + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for (i = m_creature->getThreatManager().getThreatList().begin(); i!= m_creature->getThreatManager().getThreatList().end();++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit && (pUnit->GetTypeId() == TYPEID_PLAYER)) + { + // Also needs an exception in spell system. + pUnit->CastSpell(pUnit, SPELL_GRAVITY_LAPSE_FLY, true, 0, 0, m_creature->GetGUID()); + // Use packet hack + WorldPacket data(12); + data.SetOpcode(SMSG_MOVE_SET_CAN_FLY); + data.append(pUnit->GetPackGUID()); + data << uint32(0); + pUnit->SendMessageToSet(&data, true); + } + } + } + + void RemoveGravityLapse() + { + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for (i = m_creature->getThreatManager().getThreatList().begin(); i!= m_creature->getThreatManager().getThreatList().end();++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit && (pUnit->GetTypeId() == TYPEID_PLAYER)) + { + pUnit->RemoveAurasDueToSpell(SPELL_GRAVITY_LAPSE_FLY); + pUnit->RemoveAurasDueToSpell(SPELL_GRAVITY_LAPSE_DOT); + WorldPacket data(12); + data.SetOpcode(SMSG_MOVE_UNSET_CAN_FLY); + data.append(pUnit->GetPackGUID()); + data << uint32(0); + pUnit->SendMessageToSet(&data, true); + } + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->getVictim() && !m_creature->SelectHostilTarget()) + return; + + switch(Phase) + { + case 0: + { + // *Heroic mode only: + if(Heroic) + if(PyroblastTimer < diff) + { + DoCast(m_creature, SPELL_SHOCK_BARRIER, true); + DoCast(m_creature->getVictim(), SPELL_PYROBLAST); + PyroblastTimer = 60000; + }else PyroblastTimer -= diff; + + if(FireballTimer < diff) + { + // *Normal/Heroic mode support + if(Heroic) DoCast(m_creature->getVictim(), SPELL_FIREBALL_HEROIC); + else DoCast(m_creature->getVictim(), SPELL_FIREBALL_NORMAL); + FireballTimer = 2000 + rand()%4000; + }else FireballTimer -= diff; + + if(PhoenixTimer < diff) + { + uint32 random = rand()%2 + 1; + float x = KaelLocations[random][0]; + float y = KaelLocations[random][1]; + Creature* Phoenix = m_creature->SummonCreature(CREATURE_PHOENIX, x, y, LOCATION_Z, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); + if(Phoenix) + { + Phoenix->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE); + SetThreatList(Phoenix); + } + + DoYell(SAY_PHOENIX, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_PHOENIX); + + PhoenixTimer = 40000; + }else PhoenixTimer -= diff; + + if(FlameStrikeTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + DoCast(target, SPELL_FLAMESTRIKE3, true); + FlameStrikeTimer = 20000 + rand()%5000; + + DoYell(SAY_FLAMESTRIKE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_FLAMESTRIKE); + } + }else FlameStrikeTimer -= diff; + + // Below 50% + if(m_creature->GetMaxHealth() * 0.5 > m_creature->GetHealth()) + { + m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, true); + m_creature->StopMoving(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + GravityLapseTimer = 0; + GravityLapsePhase = 0; + Phase = 1; + } + DoMeleeAttackIfReady(); + } + break; + + case 1: + { + if(GravityLapseTimer < diff) + { + switch(GravityLapsePhase) + { + case 0: + if(FirstGravityLapse) // Different yells at 50%, and at every following Gravity Lapse + { + DoYell(SAY_GRAVITY_LAPSE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_GRAVITY_LAPSE); + FirstGravityLapse = false; + if(pInstance) + { + GameObject* KaelLeft = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_KAEL_STATUE_LEFT)); + if(KaelLeft) KaelLeft->SetGoState(0); + GameObject* KaelRight = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_KAEL_STATUE_RIGHT)); + if(KaelRight) KaelRight->SetGoState(0); + } + }else + { + DoYell(SAY_RECAST_GRAVITY,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RECAST_GRAVITY); + } + DoCast(m_creature, SPELL_GRAVITY_LAPSE_INITIAL); + GravityLapseTimer = 2000 + diff;// Don't interrupt the visual spell + GravityLapsePhase = 1; + break; + + case 1: + TeleportPlayersToSelf(); + GravityLapseTimer = 1000; + GravityLapsePhase = 2; + break; + + case 2: + CastGravityLapseKnockUp(); + GravityLapseTimer = 1000; + GravityLapsePhase = 3; + break; + + case 3: + CastGravityLapseFly(); + GravityLapseTimer = 30000; + GravityLapsePhase = 4; + for(uint8 i = 0; i < 3; ++i) + { + Creature* Orb = DoSpawnCreature(CREATURE_ARCANE_SPHERE, 5, 5, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000); + if(Orb) SetThreatList(Orb); + } + DoCast(m_creature, SPELL_GRAVITY_LAPSE_CHANNEL); + break; + + case 4: + m_creature->InterruptNonMeleeSpells(false); + DoYell(SAY_TIRED,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_TIRED); + DoCast(m_creature, SPELL_POWER_FEEDBACK); + RemoveGravityLapse(); + GravityLapseTimer = 10000; + GravityLapsePhase = 0; + break; + } + }else GravityLapseTimer -= diff; + } + break; + } + } +}; + +struct MANGOS_DLL_DECL mob_felkael_flamestrikeAI : public ScriptedAI +{ + mob_felkael_flamestrikeAI(Creature *c) : ScriptedAI(c) + { + Reset(); + Heroic = c->GetMap()->IsHeroic() ? true : false; + } + + uint32 FlameStrikeTimer; + bool Heroic; + + void Reset() + { + FlameStrikeTimer = 5000; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->setFaction(14); + + DoCast(m_creature, SPELL_FLAMESTRIKE2, true); + } + + void Aggro(Unit *who) {} + void MoveInLineOfSight(Unit *who) {} + void UpdateAI(const uint32 diff) + { + if(FlameStrikeTimer < diff) + { + // *Normal/Heroic mode support + if(Heroic) DoCast(m_creature, SPELL_FLAMESTRIKE1_HEROIC, true); + else DoCast(m_creature, SPELL_FLAMESTRIKE1_NORMAL, true); + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + }else FlameStrikeTimer -= diff; + } +}; + +struct MANGOS_DLL_DECL mob_felkael_phoenixAI : public ScriptedAI +{ + mob_felkael_phoenixAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 BurnTimer; + + void Reset() + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE); + BurnTimer = 2000; + } + + void Aggro(Unit* who) {} + + void JustDied(Unit* slayer) + { + DoSpawnCreature(CREATURE_PHOENIX_EGG, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (BurnTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_PHOENIX_BURN); + BurnTimer = 2000; + }else BurnTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_felkael_phoenix_eggAI : public ScriptedAI +{ + mob_felkael_phoenix_eggAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 HatchTimer; + + void Reset() { HatchTimer = 15000; } + + void Aggro(Unit* who) {} + void MoveInLineOfSight(Unit* who) {} + + void UpdateAI(const uint32 diff) + { + if(HatchTimer < diff) + { + DoSpawnCreature(CREATURE_PHOENIX, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000); + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + }else HatchTimer -= diff; + } +}; + +struct MANGOS_DLL_DECL mob_arcane_sphereAI : public ScriptedAI +{ + mob_arcane_sphereAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 DespawnTimer; + uint32 ChangeTargetTimer; + + bool TargetLocked; + + void Reset() + { + DespawnTimer = 30000; + ChangeTargetTimer = 5000; + TargetLocked = false; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->setFaction(14); + DoCast(m_creature, SPELL_ARCANE_SPHERE_PASSIVE, true); + } + + void MoveInLineOfSight(Unit* who) + { + if(TargetLocked) + return; + if(who && who->IsHostileTo(m_creature) && (m_creature->IsWithinDistInMap(who, 25))) + StalkTarget(who); + } + + void Aggro(Unit* who) {} + + void StalkTarget(Unit* target) + { + if(!target) + return; + + m_creature->GetMotionMaster()->MoveChase(target); + TargetLocked = true; + } + + void UpdateAI(const uint32 diff) + { + if(DespawnTimer < diff) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + else DespawnTimer -= diff; + + if(!m_creature->getVictim() || !m_creature->SelectHostilTarget()) + return; + + if(ChangeTargetTimer < diff) + { + TargetLocked = false; + ChangeTargetTimer = 10000; + }else ChangeTargetTimer -= diff; + } +}; + +CreatureAI* GetAI_boss_felblood_kaelthas(Creature* c) +{ + return new boss_felblood_kaelthasAI(c); +} + +CreatureAI* GetAI_mob_arcane_sphere(Creature* c) +{ + return new mob_arcane_sphereAI(c); +} + +CreatureAI* GetAI_mob_felkael_phoenix(Creature* c) +{ + return new mob_felkael_phoenixAI(c); +} + +CreatureAI* GetAI_mob_felkael_phoenix_egg(Creature* c) +{ + return new mob_felkael_phoenix_eggAI(c); +} + +CreatureAI* GetAI_mob_felkael_flamestrike(Creature* c) +{ + return new mob_felkael_flamestrikeAI(c); +} + +void AddSC_boss_felblood_kaelthas() +{ + Script *newscript; + + newscript = new Script; + newscript->Name = "boss_felblood_kaelthas"; + newscript->GetAI = GetAI_boss_felblood_kaelthas; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_arcane_sphere"; + newscript->GetAI = GetAI_mob_arcane_sphere; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_felkael_phoenix"; + newscript->GetAI = GetAI_mob_felkael_phoenix; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_felkael_phoenix_egg"; + newscript->GetAI = GetAI_mob_felkael_phoenix_egg; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_felkael_flamestrike"; + newscript->GetAI = GetAI_mob_felkael_flamestrike; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/magisters_terrace/boss_priestess_delrissa.cpp b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_priestess_delrissa.cpp new file mode 100644 index 00000000000..94b13cd5cd5 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_priestess_delrissa.cpp @@ -0,0 +1,1367 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Priestess_Delrissa +SD%Complete: 45 +SDComment: No Heroic support yet. Needs further testing. Several scripts for pets disabled, not seem to require any special script. +SDCategory: Magister's Terrace +EndScriptData */ + +#include "precompiled.h" +#include "def_magisters_terrace.h" + +#define SAY_AGGRO "Annihilate them!" +#define SOUND_AGGRO 12395 + +struct Speech +{ + const char* text; + uint32 sound; +}; + +static Speech LackeyDeath[]= +{ + {"Oh, the horror.", 12398}, + {"Well, aren't you lucky?", 12400}, + {"Now I'm getting annoyed.", 12401}, + {"Lackies be damned! I'll finish you myself!", 12403}, +}; + +static Speech PlayerDeath[]= +{ + {"I call that a good start.", 12405}, + {"I could have sworn there were more of you.", 12407}, + {"Not really much of a group, anymore, is it?", 12409}, + {"One is such a lonely number", 12410}, + {"It's been a kick, really", 12411}, +}; + +#define SAY_DEATH "Not what I had... planned..." +#define SOUND_DEATH 12397 + +#define SPELL_DISPEL_MAGIC 27609 +#define SPELL_FLASH_HEAL 17843 +#define SPELL_SW_PAIN_NORMAL 14032 +#define SPELL_SW_PAIN_HEROIC 15654 +#define SPELL_SHIELD 44291 +#define SPELL_RENEW_NORMAL 44174 +#define SPELL_RENEW_HEROIC 46192 + +#define ORIENT 4.98 +#define POS_Z -19.9215 + +float LackeyLocations[4][2]= +{ + {123.77, 17.6007}, + {131.731, 15.0827}, + {121.563, 15.6213}, + {129.988, 17.2355}, +}; + +const uint32 AddEntry[8]= +{ + 24557, //Kagani Nightstrike + 24558, //Elris Duskhallow + 24554, //Eramas Brightblaze + 24561, //Yazzaj + 24559, //Warlord Salaris + 24555, //Garaxxas + 24553, //Apoko + 24556, //Zelfan +}; + +struct Add +{ + Add(uint32 _entry, uint64 _guid) + { + entry = _entry; + guid = _guid; + } + + uint32 entry; + uint64 guid; +}; + +struct MANGOS_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI +{ + boss_priestess_delrissaAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Adds.clear(); + Reset(); + SummonAdds(); + Heroic = c->GetMap()->IsHeroic(); + } + + ScriptedInstance* pInstance; + + std::vector Adds; + + uint8 LackeysKilled; + uint8 PlayersKilled; + + uint32 HealTimer; + uint32 RenewTimer; + uint32 ShieldTimer; + uint32 SWPainTimer; + uint32 DispelTimer; + + uint32 CombatPulseTimer; // Periodically puts all players in the instance in combat + + bool Heroic; + + void Reset() + { + LackeysKilled = 0; + PlayersKilled = 0; + + HealTimer = 15000; + RenewTimer = 10000; + ShieldTimer = 2000; + SWPainTimer = 5000; + DispelTimer = 7500; + + CombatPulseTimer = 5000; + + CheckAdds(); + + if(pInstance) + { + pInstance->SetData(DATA_DELRISSA_EVENT, NOT_STARTED); + pInstance->SetData(DATA_DELRISSA_DEATH_COUNT, 0); + } + else error_log(ERROR_INST_DATA); + } + + void Aggro(Unit* who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + + for(uint8 i = 0; i < Adds.size(); ++i) + if(Unit* pAdd = Unit::GetUnit(*m_creature, Adds[i]->guid)) + pAdd->AddThreat(who, 1.0f); + } + + void SummonAdds() + { + std::vector AddList; + for(uint8 i = 0; i < 8; ++i) + AddList.push_back(AddEntry[i]); + + while(AddList.size() > 4) + AddList.erase(AddList.begin() + rand()%AddList.size()); + + for(uint8 i = 0; i < AddList.size(); ++i) + { + Creature* pAdd = m_creature->SummonCreature(AddList[i], LackeyLocations[i][0], LackeyLocations[i][1], POS_Z, ORIENT, TEMPSUMMON_DEAD_DESPAWN, 0); + if(pAdd) + { + Add* nAdd = new Add(AddList[i], pAdd->GetGUID()); + Adds.push_back(nAdd); + } + } + } + + void CheckAdds() + { + if(Adds.empty()) + return; + + for(uint8 i = 0; i < Adds.size(); ++i) + { + bool resummon = true; + Creature* pAdd = ((Creature*)Unit::GetUnit(*m_creature, Adds[i]->guid)); + if(pAdd && pAdd->isAlive()) + { + pAdd->AI()->EnterEvadeMode(); // Force them out of combat and reset if they are in combat. + resummon = false; + } + if(resummon) + { + pAdd = m_creature->SummonCreature(Adds[i]->entry, LackeyLocations[i][0], LackeyLocations[i][1], POS_Z, ORIENT, TEMPSUMMON_DEAD_DESPAWN, 0); + Add* nAdd = new Add(Adds[i]->entry, pAdd->GetGUID()); + Adds.erase(Adds.begin() + i); + Adds.push_back(nAdd); + } + } + } + + void KilledUnit(Unit* victim) + { + if(victim->GetTypeId() != TYPEID_PLAYER) + return; + + DoYell(PlayerDeath[PlayersKilled].text, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, PlayerDeath[PlayersKilled].sound); + if( PlayersKilled < 4 ) + ++PlayersKilled; + } + + void KilledLackey() + { + DoYell(LackeyDeath[LackeysKilled].text, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, LackeyDeath[LackeysKilled].sound); + if( LackeysKilled < 3 ) + ++LackeysKilled; + } + + void JustDied(Unit* killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + CheckLootable(); + + if(!pInstance) + { + error_log(ERROR_INST_DATA); + return; + } + + pInstance->SetData(DATA_DELRISSA_DEATH_COUNT, 1); + pInstance->SetData(DATA_DELRISSA_EVENT, DONE); + if(GameObject* Door = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_DELRISSA_DOOR))) + Door->SetGoState(0); + } + + void CheckLootable() + { + if(LackeysKilled > 4) + m_creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + else + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(HealTimer < diff) + { + uint32 health = m_creature->GetHealth(); + Unit* target = m_creature; + for(uint8 i = 0; i < Adds.size(); ++i) + if(Unit* pAdd = Unit::GetUnit(*m_creature, Adds[i]->guid)) + if(pAdd->isAlive() && pAdd->GetHealth() < health) + target = pAdd; + + DoCast(target, SPELL_FLASH_HEAL); + HealTimer = 15000; + }else HealTimer -= diff; + + if(RenewTimer < diff) + { + Unit* target = m_creature; + if(rand()%2 == 1) + { + std::vector::iterator itr = Adds.begin() + rand()%Adds.size(); + Unit* pAdd = Unit::GetUnit(*m_creature, (*itr)->guid); + if(pAdd && pAdd->isAlive()) + target = pAdd; + } + DoCast(target,Heroic ? SPELL_RENEW_HEROIC : SPELL_RENEW_NORMAL); + RenewTimer = 5000; + }else RenewTimer -= diff; + + if(ShieldTimer < diff) + { + Unit* target = m_creature; + if(rand()%2 == 1) + { + std::vector::iterator itr = Adds.begin() + rand()%Adds.size(); + if(Unit* pAdd = Unit::GetUnit(*m_creature, (*itr)->guid)) + if(!pAdd->HasAura(SPELL_SHIELD, 0) && pAdd->isAlive()) + target = pAdd; + } + DoCast(target, SPELL_SHIELD); + ShieldTimer = 7500; + }else ShieldTimer -= diff; + + if(DispelTimer < diff) + { + Unit* target = NULL; + bool friendly = false; + if(rand()%2 == 1) + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + else + { + friendly = true; + if(rand()%2 == 1) + target = m_creature; + else + { + std::vector::iterator itr = Adds.begin() + rand()%Adds.size(); + Unit* pAdd = Unit::GetUnit(*m_creature, (*itr)->guid); + if(pAdd && pAdd->isAlive()) + target = pAdd; + } + } + if(target) + { + DoCast(target, SPELL_DISPEL_MAGIC); + DispelTimer = 12000; + } + }else DispelTimer -= diff; + + if(SWPainTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0),Heroic ? SPELL_SW_PAIN_HEROIC : SPELL_SW_PAIN_NORMAL); + SWPainTimer = 10000; + }else SWPainTimer -= diff; + + /* + if(CombatPulseTimer < diff) + { + DoZoneInCombat(); + for(uint8 i = 0; i < Adds.size(); ++i) + { + if(Unit* pAdd = Unit::GetUnit(*m_creature, Add[i]->guid)) + if(pAdd->isAlive()) + DoZoneInCombat(pAdd); + } + + CombatPulseTimer = 10000; + }else CombatPulseTimer -= diff;*/ + + DoMeleeAttackIfReady(); + } +}; + +#define SPELL_HEALING_POTION 15503 + +struct MANGOS_DLL_DECL boss_priestess_guestAI : public ScriptedAI +{ + boss_priestess_guestAI(Creature* c) : ScriptedAI(c) + { + Group.clear(); + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + AcquireGUIDs(); + } + + ScriptedInstance* pInstance; + + std::vector Group; + + uint32 ResetThreatTimer; + + bool UsedPotion; + + void Reset() + { + UsedPotion = false; + + ResetThreatTimer = 5000 + rand()%15000; // These guys like to switch targets often, and are not meant to be tanked. + } + + void Aggro(Unit* who) {} + + void JustDied(Unit* killer) + { + if(!pInstance) + { + error_log(ERROR_INST_DATA); + return; + } + + Creature* Delrissa = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_DELRISSA))); + if(Delrissa) + { + ((boss_priestess_delrissaAI*)Delrissa->AI())->KilledLackey(); + if(!Delrissa->isAlive() && pInstance->GetData(DATA_DELRISSA_DEATH_COUNT) > 3) + ((boss_priestess_delrissaAI*)Delrissa->AI())->CheckLootable(); + + pInstance->SetData(DATA_DELRISSA_DEATH_COUNT, 1); + } + } + + void KilledUnit(Unit* victim) + { + if(!pInstance) + { + error_log(ERROR_INST_DATA); + return; + } + + Creature* Delrissa = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_DELRISSA))); + if(Delrissa) + Delrissa->AI()->KilledUnit(victim); + } + + void AcquireGUIDs() + { + if(!pInstance) + { + error_log(ERROR_INST_DATA); + return; + } + + Creature* Delrissa = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_DELRISSA))); + if(Delrissa) + { + Group = ((boss_priestess_delrissaAI*)Delrissa->AI())->Adds; + Add* dAdd = new Add(Delrissa->GetEntry(), Delrissa->GetGUID()); + Group.push_back(dAdd); + } + } + + void UpdateAI(const uint32 diff) + { + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 25) && !UsedPotion) + { + DoCast(m_creature, SPELL_HEALING_POTION, true); + UsedPotion = true; + } + + if(ResetThreatTimer < diff) + { + DoResetThreat(); + ResetThreatTimer = 5000 + rand()%15000; + }else ResetThreatTimer -= diff; + } +}; + +#define SPELL_KIDNEY_SHOT 27615 +#define SPELL_GOUGE 12540 +#define SPELL_KICK 27613 +#define SPELL_VANISH 44290 +#define SPELL_BACKSTAB 15657 +#define SPELL_EVISCERATE 27611 + +struct MANGOS_DLL_DECL boss_kagani_nightstrikeAI : public boss_priestess_guestAI +{ + //Rogue + boss_kagani_nightstrikeAI(Creature *c) : boss_priestess_guestAI(c) {} + + uint32 Gouge_Timer; + uint32 Kick_Timer; + uint32 Vanish_Timer; + uint32 Eviscerate_Timer; + uint32 Wait_Timer; + bool InVanish; + + void Reset() + { + Gouge_Timer = 5500; + Kick_Timer = 7000; + Vanish_Timer = 2000; + Eviscerate_Timer = 6000; + Wait_Timer = 5000; + InVanish = false; + m_creature->SetVisibility(VISIBILITY_ON); + + boss_priestess_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_priestess_guestAI::UpdateAI(diff); + + if(Vanish_Timer < diff) + { + m_creature->SetVisibility(VISIBILITY_OFF); // ...? Hacklike + DoCast(m_creature, SPELL_VANISH); + InVanish = true; + Vanish_Timer = 30000; + Wait_Timer = 10000; + DoResetThreat(); + m_creature->AddThreat(SelectUnit(SELECT_TARGET_RANDOM, 0), 1000.0f); + }else Vanish_Timer -= diff; + + if(InVanish) + if(Wait_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_BACKSTAB, true); + DoCast(m_creature->getVictim(), SPELL_KIDNEY_SHOT, true); + m_creature->SetVisibility(VISIBILITY_ON); // ...? Hacklike + InVanish = false; + }else Wait_Timer -= diff; + + if(Gouge_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_GOUGE); + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-100); + Gouge_Timer = 5500; + }else Gouge_Timer -= diff; + + if(Kick_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_KICK); + Kick_Timer = 7000; + }else Kick_Timer -= diff; + + if(Eviscerate_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_EVISCERATE); + Eviscerate_Timer = 4000; + }else Eviscerate_Timer -= diff; + + if(!InVanish) + DoMeleeAttackIfReady(); + } +}; + +#define SPELL_IMMOLATE 44267 +#define SPELL_SHADOW_BOLT 12471 +#define SPELL_SEED_OF_CORRUPTION 44141 +#define SPELL_CURSE_OF_AGONY 14875 +#define SPELL_FEAR 38595 +#define SPELL_IMP_FIREBALL 44164 +#define SPELL_SUMMON_IMP 44163 + +//#define CREATURE_IMP 44163 +//#define CREATURE_FIZZLE 24656 + +/*struct MANGOS_DLL_DECL mob_fizzleAI : public ScriptedAI +{ + mob_fizzleAI(Creature *c) : ScriptedAI(c) + { + Reset(); + } + + uint64 EllrisGUID; + uint32 Firebal_Timer; + + void Reset() { EllrisGUID = 0; } + + void KilledUnit(Unit* victim); + void JustDied(Unit* killer); + + void Aggro(Unit* who){} + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Chain cast + if (!m_creature->IsNonMeleeSpellCasted(false)) + DoCast(m_creature->getVictim(),SPELL_IMP_FIREBALL); + else DoMeleeAttackIfReady(); + } +};*/ + +struct MANGOS_DLL_DECL boss_ellris_duskhallowAI : public boss_priestess_guestAI +{ + //Warlock + boss_ellris_duskhallowAI(Creature *c) : boss_priestess_guestAI(c) + { + } + + bool HasSummonedImp; + + uint32 Immolate_Timer; + uint32 Shadow_Bolt_Timer; + uint32 Seed_of_Corruption_Timer; + uint32 Curse_of_Agony_Timer; + uint32 Fear_Timer; + + void Reset() + { + //HasSummonedImp = false; + + Immolate_Timer = 6000; + Shadow_Bolt_Timer = 3000; + Seed_of_Corruption_Timer = 2000; + Curse_of_Agony_Timer = 1000; + Fear_Timer = 10000; + + boss_priestess_guestAI::Reset(); + } + + void JustDied(Unit* killer) + { + boss_priestess_guestAI::JustDied(killer); + } + + void UpdateAI(const uint32 diff) + { + if(!HasSummonedImp) + { + //Imp will not despawn unless it's killed, even if owner dies, this is correct way. + DoCast(m_creature,SPELL_SUMMON_IMP); + HasSummonedImp = true; + } + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_priestess_guestAI::UpdateAI(diff); + + if(Immolate_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_IMMOLATE); + Immolate_Timer = 6000; + }else Immolate_Timer -= diff; + + if(Shadow_Bolt_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOW_BOLT); + Shadow_Bolt_Timer = 5000; + }else Shadow_Bolt_Timer -= diff; + + if(Seed_of_Corruption_Timer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_SEED_OF_CORRUPTION); + Seed_of_Corruption_Timer = 10000; + }else Seed_of_Corruption_Timer -= diff; + + if(Curse_of_Agony_Timer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_CURSE_OF_AGONY); + Curse_of_Agony_Timer = 13000; + }else Curse_of_Agony_Timer -= diff; + + if(Fear_Timer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_FEAR); + Fear_Timer = 10000; + }else Fear_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +/*void mob_fizzleAI::JustDied(Unit* killer) +{ + if(Creature* Ellris = ((Creature*)Unit::GetUnit(*m_creature, EllrisGUID))) + ((boss_ellris_duskhallowAI*)Ellris->AI())->ImpGUID = 0; +} + +void mob_fizzleAI::KilledUnit(Unit* victim) +{ + if(Creature* Ellris = ((Creature*)Unit::GetUnit(*m_creature, EllrisGUID))) + ((boss_ellris_duskhallowAI*)Ellris->AI())->KilledUnit(victim); +}*/ + +#define SPELL_KNOCKDOWN 11428 +#define SPELL_SNAP_KICK 46182 + +struct MANGOS_DLL_DECL boss_eramas_brightblazeAI : public boss_priestess_guestAI +{ + //Monk + boss_eramas_brightblazeAI(Creature *c) : boss_priestess_guestAI(c) {} + + uint32 Knockdown_Timer; + uint32 Snap_Kick_Timer; + + void Reset() + { + Knockdown_Timer = 6000; + Snap_Kick_Timer = 4500; + + boss_priestess_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_priestess_guestAI::UpdateAI(diff); + + if(Knockdown_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCKDOWN); + Knockdown_Timer = 6000; + }else Knockdown_Timer -= diff; + + if(Snap_Kick_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SNAP_KICK); + Snap_Kick_Timer = 4500; + }else Snap_Kick_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +#define SPELL_POLYMORPH 13323 +#define SPELL_ICE_BLOCK 27619 +#define SPELL_BLIZZARD 44178 +#define SPELL_ICE_LANCE 46194 +#define SPELL_CONE_OF_COLD 38384 +#define SPELL_FROSTBOLT 15043 +#define SPELL_BLINK 14514 + +struct MANGOS_DLL_DECL boss_yazzaiAI : public boss_priestess_guestAI +{ + //Mage + boss_yazzaiAI(Creature *c) : boss_priestess_guestAI(c) {} + + bool HasIceBlocked; + + uint32 Polymorph_Timer; + uint32 Ice_Block_Timer; + uint32 Wait_Timer; + uint32 Blizzard_Timer; + uint32 Ice_Lance_Timer; + uint32 Cone_of_Cold_Timer; + uint32 Frostbolt_Timer; + uint32 Blink_Timer; + + void Reset() + { + HasIceBlocked = false; + + Polymorph_Timer = 1000; + Ice_Block_Timer = 20000; + Wait_Timer = 10000; + Blizzard_Timer = 8000; + Ice_Lance_Timer = 12000; + Cone_of_Cold_Timer = 10000; + Frostbolt_Timer = 3000; + Blink_Timer = 8000; + + boss_priestess_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_priestess_guestAI::UpdateAI(diff); + + if(Polymorph_Timer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + DoCast(target, SPELL_POLYMORPH); + m_creature->getThreatManager().modifyThreatPercent(target,-100); + Polymorph_Timer = 20000; + } + }else Polymorph_Timer -= diff; + + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 35) && !HasIceBlocked) + { + DoCast(m_creature, SPELL_ICE_BLOCK); + HasIceBlocked = true; + } + + if(Blizzard_Timer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_BLIZZARD); + Blizzard_Timer = 8000; + }else Blizzard_Timer -= diff; + + if(Ice_Lance_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ICE_LANCE); + Ice_Lance_Timer = 12000; + }else Ice_Lance_Timer -= diff; + + if(Cone_of_Cold_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CONE_OF_COLD); + Cone_of_Cold_Timer = 10000; + }else Cone_of_Cold_Timer -= diff; + + if(Frostbolt_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FROSTBOLT); + Frostbolt_Timer = 8000; + }else Frostbolt_Timer -= diff; + + if(Blink_Timer < diff) + { + bool InMeleeRange = false; + std::list& t_list = m_creature->getThreatManager().getThreatList(); + for(std::list::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr) + { + if(Unit* target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid())) + //if in melee range + if (target->IsWithinDistInMap(m_creature, 5)) + { + InMeleeRange = true; + break; + } + } + //if anybody is in melee range than escape by blink + if(InMeleeRange) + DoCast(m_creature, SPELL_BLINK); + + Blink_Timer = 8000; + }else Blink_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +#define SPELL_INTERCEPT_STUN 27577 +#define SPELL_DISARM 27581 +#define SPELL_PIERCING_HOWL 23600 +#define SPELL_FRIGHTENING_SHOUT 19134 +#define SPELL_HAMSTRING 27584 +#define SPELL_BATTLE_SHOUT 27578 +#define SPELL_MORTAL_STRIKE 44268 + +struct MANGOS_DLL_DECL boss_warlord_salarisAI : public boss_priestess_guestAI +{ + //Warrior + boss_warlord_salarisAI(Creature *c) : boss_priestess_guestAI(c) {} + + uint32 Intercept_Stun_Timer; + uint32 Disarm_Timer; + uint32 Piercing_Howl_Timer; + uint32 Frightening_Shout_Timer; + uint32 Hamstring_Timer; + uint32 Mortal_Strike_Timer; + + void Reset() + { + Intercept_Stun_Timer = 500; + Disarm_Timer = 6000; + Piercing_Howl_Timer = 10000; + Frightening_Shout_Timer = 18000; + Hamstring_Timer = 4500; + Mortal_Strike_Timer = 8000; + DoCast(m_creature, SPELL_BATTLE_SHOUT); + boss_priestess_guestAI::Reset(); + } + + void Aggro(Unit* who) + { + DoCast(m_creature, SPELL_BATTLE_SHOUT); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_priestess_guestAI::UpdateAI(diff); + + if(Intercept_Stun_Timer < diff) + { + bool InMeleeRange = false; + std::list& t_list = m_creature->getThreatManager().getThreatList(); + for(std::list::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr) + { + if(Unit* target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid())) + //if in melee range + if (target->IsWithinDistInMap(m_creature, 5)) + { + InMeleeRange = true; + break; + } + } + //if nobody is in melee range than try to use Intercept + if(!InMeleeRange) + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_INTERCEPT_STUN); + Intercept_Stun_Timer = 10000; + }else Intercept_Stun_Timer -= diff; + + if(Disarm_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_DISARM); + Disarm_Timer = 6000; + }else Disarm_Timer -= diff; + + if(Hamstring_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_HAMSTRING); + Hamstring_Timer = 4500; + }else Hamstring_Timer -= diff; + + if(Mortal_Strike_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_MORTAL_STRIKE); + Mortal_Strike_Timer = 4500; + }else Mortal_Strike_Timer -= diff; + + if(Piercing_Howl_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_PIERCING_HOWL); + Piercing_Howl_Timer = 10000; + }else Piercing_Howl_Timer -= diff; + + if(Frightening_Shout_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FRIGHTENING_SHOUT); + Frightening_Shout_Timer = 18000; + }else Frightening_Shout_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +#define SPELL_AIMED_SHOT 44271 +#define SPELL_SHOOT 15620 +#define SPELL_CONCUSSIVE_SHOT 27634 +#define TRIGGER_CONCUSSIVE_SHOT 19410 +#define SPELL_MULTI_SHOT 31942 +#define SPELL_WING_CLIP 44286 +#define SPELL_FREEZING_TRAP 44136 + +#define CREATURE_SLIVER 24552 + +/*struct MANGOS_DLL_DECL mob_sliverAI : public ScriptedAI +{ + mob_sliverAI(Creature *c) : ScriptedAI(c) + { + Reset(); + } + + uint64 GaraxxasGUID; + + void Reset() { GaraxxasGUID = 0; } + + void KilledUnit(Unit* victim); + void JustDied(Unit* killer); + + void Aggro(Unit* who){} + +};*/ + +struct MANGOS_DLL_DECL boss_garaxxasAI : public boss_priestess_guestAI +{ + //Hunter + boss_garaxxasAI(Creature *c) : boss_priestess_guestAI(c) {} + + //uint64 SliverGUID; + bool HasSummonedSliver; + + uint32 Aimed_Shot_Timer; + uint32 Shoot_Timer; + uint32 Concussive_Shot_Timer; + uint32 Multi_Shot_Timer; + uint32 Wing_Clip_Timer; + uint32 Freezing_Trap_Timer; + + void Reset() + { + //SliverGUID = 0; + //HasSummonedSliver = false; + + Aimed_Shot_Timer = 6000; + Shoot_Timer = 2500; + Concussive_Shot_Timer = 8000; + Multi_Shot_Timer = 10000; + Wing_Clip_Timer = 4000; + Freezing_Trap_Timer = 15000; + + boss_priestess_guestAI::Reset(); + } + + void JustDied(Unit* killer) + { + boss_priestess_guestAI::JustDied(killer); + } + + void UpdateAI(const uint32 diff) + { + if(!HasSummonedSliver) + { + Creature* Sliver = m_creature->SummonCreature(CREATURE_SLIVER, 0, 0, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + if(Sliver) + { + //((mob_sliverAI*)Sliver->AI())->GaraxxasGUID = m_creature->GetGUID(); + //SliverGUID = Sliver->GetGUID(); + HasSummonedSliver = true; + } + } + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_priestess_guestAI::UpdateAI(diff); + + if(m_creature->IsWithinDistInMap(m_creature->getVictim(), 5)) + { + if(Wing_Clip_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_WING_CLIP); + Wing_Clip_Timer = 4000; + }else Wing_Clip_Timer -= diff; + + if(Freezing_Trap_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FREEZING_TRAP); + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-100); + Freezing_Trap_Timer = 30000; + }else Freezing_Trap_Timer -= diff; + + if(!m_creature->getVictim()->hasUnitState(UNIT_STAT_STUNDED | UNIT_STAT_ROOT | UNIT_STAT_CONFUSED | UNIT_STAT_DISTRACTED)) + DoMeleeAttackIfReady(); + }else + { + if(Concussive_Shot_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CONCUSSIVE_SHOT); + Concussive_Shot_Timer = 8000; + }else Concussive_Shot_Timer -= diff; + + if(Multi_Shot_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_MULTI_SHOT); + Multi_Shot_Timer = 10000; + }else Multi_Shot_Timer -= diff; + + if(Aimed_Shot_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_AIMED_SHOT); + Aimed_Shot_Timer = 6000; + }else Aimed_Shot_Timer -= diff; + + if(Shoot_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SHOOT); + Shoot_Timer = 2500; + }else Shoot_Timer -= diff; + } + } +}; + +/*void mob_sliverAI::JustDied(Unit* killer) +{ + if(Creature* Garaxxas = ((Creature*)Unit::GetUnit(*m_creature, GaraxxasGUID))) + ((boss_garaxxasAI*)Garaxxas->AI())->SliverGUID = 0; +} + +void mob_sliverAI::KilledUnit(Unit* victim) +{ + if(Creature* Garaxxas = ((Creature*)Unit::GetUnit(*m_creature, GaraxxasGUID))) + ((boss_garaxxasAI*)Garaxxas->AI())->KilledUnit(victim); +}*/ + +#define SPELL_WINDFURY_TOTEM 27621 +#define SPELL_WAR_STOMP 46026 +#define SPELL_PURGE 27626 +#define SPELL_LESSER_HEALING_WAVE 44256 +#define SPELL_FROST_SHOCK 21401 +#define SPELL_FIRE_NOVA_TOTEM 44257 +#define SPELL_EARTHBIND_TOTEM 15786 + +struct MANGOS_DLL_DECL boss_apokoAI : public boss_priestess_guestAI +{ + //Shaman + boss_apokoAI(Creature *c) : boss_priestess_guestAI(c) {} + + uint32 Totem_Timer; + uint8 Totem_Amount; + uint32 War_Stomp_Timer; + uint32 Purge_Timer; + uint32 Healing_Wave_Timer; + uint32 Frost_Shock_Timer; + + void Reset() + { + Totem_Timer = 2000; + Totem_Amount = 1; + War_Stomp_Timer = 10000; + Purge_Timer = 8000; + Healing_Wave_Timer = 5000; + Frost_Shock_Timer = 7000; + + boss_priestess_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_priestess_guestAI::UpdateAI(diff); + + if(Totem_Timer < diff) + { + switch(rand()%3) + { + case 0: + DoCast(m_creature, SPELL_WINDFURY_TOTEM); + break; + case 1: + DoCast(m_creature, SPELL_FIRE_NOVA_TOTEM); + break; + case 2: + DoCast(m_creature, SPELL_EARTHBIND_TOTEM); + break; + } + ++Totem_Amount; + Totem_Timer = Totem_Amount*2000; + }else Totem_Timer -= diff; + + if(War_Stomp_Timer < diff) + { + DoCast(m_creature, SPELL_WAR_STOMP); + War_Stomp_Timer = 10000; + }else War_Stomp_Timer -= diff; + + if(Purge_Timer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_PURGE); + Purge_Timer = 15000; + }else Purge_Timer -= diff; + + if(Frost_Shock_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FROST_SHOCK); + Frost_Shock_Timer = 7000; + }else Frost_Shock_Timer -= diff; + + if(Healing_Wave_Timer < diff) + { + // std::vector::iterator itr = Group.begin() + rand()%Group.size(); + // uint64 guid = (*itr)->guid; + // if(guid) + // { + // Unit* pAdd = Unit::GetUnit(*m_creature, (*itr)->guid); + // if(pAdd && pAdd->isAlive()) + // { + DoCast(m_creature, SPELL_LESSER_HEALING_WAVE); + Healing_Wave_Timer = 5000; + // } + // } + }else Healing_Wave_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +#define SPELL_GOBLIN_DRAGON_GUN 44272 +#define SPELL_ROCKET_LAUNCH 44137 +#define SPELL_RECOMBOBULATE 44274 +#define SPELL_HIGH_EXPLOSIVE_SHEEP 44276 +#define SPELL_FEL_IRON_BOMB 46024 +#define SPELL_SHEEP_EXPLOSION 44279 + +#define CREATURE_EXPLOSIVE_SHEEP 24715 + +struct MANGOS_DLL_DECL boss_zelfanAI : public boss_priestess_guestAI +{ + //Engineer + boss_zelfanAI(Creature *c) : boss_priestess_guestAI(c) {} + + uint32 Goblin_Dragon_Gun_Timer; + uint32 Rocket_Launch_Timer; + uint32 Recombobulate_Timer; + uint32 High_Explosive_Sheep_Timer; + uint32 Fel_Iron_Bomb_Timer; + + void Reset() + { + Goblin_Dragon_Gun_Timer = 20000; + Rocket_Launch_Timer = 7000; + Recombobulate_Timer = 4000; + High_Explosive_Sheep_Timer = 10000; + Fel_Iron_Bomb_Timer = 15000; + + boss_priestess_guestAI::Reset(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + boss_priestess_guestAI::UpdateAI(diff); + + if(Goblin_Dragon_Gun_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_GOBLIN_DRAGON_GUN); + Goblin_Dragon_Gun_Timer = 10000; + }else Goblin_Dragon_Gun_Timer -= diff; + + if(Rocket_Launch_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ROCKET_LAUNCH); + Rocket_Launch_Timer = 9000; + }else Rocket_Launch_Timer -= diff; + + if(Fel_Iron_Bomb_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FEL_IRON_BOMB); + Fel_Iron_Bomb_Timer = 15000; + }else Fel_Iron_Bomb_Timer -= diff; + + if(Recombobulate_Timer < diff) + { + for(uint8 i = 0; i < Group.size(); ++i) + if(Unit* pAdd = Unit::GetUnit(*m_creature, Group[i]->guid)) + if(pAdd->IsPolymorphed()) + { + DoCast(pAdd, SPELL_RECOMBOBULATE); + break; + } + }else Recombobulate_Timer -= diff; + + if(High_Explosive_Sheep_Timer < diff) + { + DoCast(m_creature, SPELL_HIGH_EXPLOSIVE_SHEEP); + High_Explosive_Sheep_Timer = 65000; + }else High_Explosive_Sheep_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//struct MANGOS_DLL_DECL mob_high_explosive_sheepAI : public ScriptedAI +//{ +// mob_high_explosive_sheepAI(Creature *c) : ScriptedAI(c) {Reset();} +// +// uint32 Explosion_Timer; +// +// void Reset() +// { +// Explosion_Timer = 60000; +// } +// +// void JustDied(Unit *Killer){} +// +// void Aggro(Unit *who){} +// +// void UpdateAI(const uint32 diff) +// { +// if(Explosion_Timer < diff) +// { +// DoCast(m_creature->getVictim(), SPELL_SHEEP_EXPLOSION); +// }else +// Explosion_Timer -= diff; +// } +//}; + +/*CreatureAI* GetAI_mob_sliver(Creature *_Creature) +{ + return new mob_sliverAI (_Creature); +};*/ + +//CreatureAI* GetAI_mob_high_explosive_sheep(Creature *_Creature) +//{ +// return new mob_high_explosive_sheepAI (_Creature); +//}; + +/*CreatureAI* GetAI_mob_fizzle(Creature *_Creature) +{ + return new mob_fizzleAI (_Creature); +};*/ + +CreatureAI* GetAI_boss_priestess_delrissa(Creature *_Creature) +{ + return new boss_priestess_delrissaAI (_Creature); +} + +CreatureAI* GetAI_boss_kagani_nightstrike(Creature *_Creature) +{ + return new boss_kagani_nightstrikeAI (_Creature); +} + +CreatureAI* GetAI_ellris_duskhallow(Creature *_Creature) +{ + return new boss_ellris_duskhallowAI (_Creature); +} + +CreatureAI* GetAI_eramas_brightblaze(Creature *_Creature) +{ + return new boss_eramas_brightblazeAI (_Creature); +} + +CreatureAI* GetAI_yazzai(Creature *_Creature) +{ + return new boss_yazzaiAI (_Creature); +} + +CreatureAI* GetAI_warlord_salaris(Creature *_Creature) +{ + return new boss_warlord_salarisAI (_Creature); +} + +CreatureAI* GetAI_garaxxas(Creature *_Creature) +{ + return new boss_garaxxasAI (_Creature); +} + +CreatureAI* GetAI_apoko(Creature *_Creature) +{ + return new boss_apokoAI (_Creature); +} + +CreatureAI* GetAI_zelfan(Creature *_Creature) +{ + return new boss_zelfanAI (_Creature); +} + +void AddSC_boss_priestess_delrissa() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_priestess_delrissa"; + newscript->GetAI = GetAI_boss_priestess_delrissa; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_kagani_nightstrike"; + newscript->GetAI = GetAI_boss_kagani_nightstrike; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_ellris_duskhallow"; + newscript->GetAI = GetAI_ellris_duskhallow; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_eramas_brightblaze"; + newscript->GetAI = GetAI_eramas_brightblaze; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_yazzai"; + newscript->GetAI = GetAI_yazzai; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_warlord_salaris"; + newscript->GetAI = GetAI_warlord_salaris; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_garaxxas"; + newscript->GetAI = GetAI_garaxxas; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_apoko"; + newscript->GetAI = GetAI_apoko; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_zelfan"; + newscript->GetAI = GetAI_zelfan; + m_scripts[nrscripts++] = newscript; + + /*newscript = new Script; + newscript->Name="mob_high_explosive_sheep"; + newscript->GetAI = GetAI_mob_high_explosive_sheep; + m_scripts[nrscripts++] = newscript;*/ + + /*newscript = new Script; + newscript->Name="mob_fizzle"; + newscript->GetAI = GetAI_mob_fizzle; + m_scripts[nrscripts++] = newscript;*/ + + /*newscript = new Script; + newscript->Name="mob_sliver"; + newscript->GetAI = GetAI_mob_sliver; + m_scripts[nrscripts++] = newscript;*/ +} diff --git a/src/bindings/scripts/scripts/zone/magisters_terrace/boss_selin_fireheart.cpp b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_selin_fireheart.cpp new file mode 100644 index 00000000000..76d4faed15f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_selin_fireheart.cpp @@ -0,0 +1,385 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Selin_Fireheart +SD%Complete: 99 +SDComment: Heroic and Normal Support. Needs further testing. +SDCategory: Magister's Terrace +EndScriptData */ + +#include "precompiled.h" +#include "def_magisters_terrace.h" + +#define SAY_AGGRO "You only waste my time!" +#define SOUND_AGGRO 12378 + +#define SAY_ENERGY "My hunger knows no bounds! " +#define SOUND_ENERGY 12381 + +#define SAY_EMPOWERED "Yes! I am a god!" +#define SOUND_EMPOWERED 12382 + +#define SAY_KILL_1 "Enough distractions!" +#define SOUND_KILL_1 12388 + +#define SAY_KILL_2 "I am invincible!" +#define SOUND_KILL_2 12385 + +#define SAY_DEATH "No! More... I must have more!" +#define SOUND_DEATH 12383 + +//Crystal efect spells +#define SPELL_FEL_CRYSTAL_COSMETIC 44374 +#define SPELL_FEL_CRYSTAL_DUMMY 44329 +#define SPELL_FEL_CRYSTAL_VISUAL 44355 +#define SPELL_MANA_RAGE 44320 // This spell triggers 44321, which changes scale and regens mana Requires an entry in spell_script_target + +//Selin's spells +#define SPELL_DRAIN_LIFE 44294 +#define SPELL_FEL_EXPLOSION 44314 + +#define SPELL_DRAIN_MANA 46153 // Heroic only + +#define CRYSTALS_NUMBER 5 +#define DATA_CRYSTALS 6 + +#define CREATURE_FEL_CRYSTAL 24722 + +struct MANGOS_DLL_DECL boss_selin_fireheartAI : public ScriptedAI +{ + boss_selin_fireheartAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + + Crystals.clear(); + // GUIDs per instance is static, so we only need to load them once. + if(pInstance) + { + uint32 size = pInstance->GetData(DATA_FEL_CRYSTAL_SIZE); + for(uint8 i = 0; i < size; ++i) + { + uint64 guid = pInstance->GetData64(DATA_FEL_CRYSTAL); + outstring_log("Selin: Adding Fel Crystal %u to list", guid); + Crystals.push_back(guid); + } + } + Reset(); + Heroic = c->GetMap()->IsHeroic() ? true : false; + } + + ScriptedInstance* pInstance; + + std::list Crystals; + + uint32 DrainLifeTimer; + uint32 DrainManaTimer; + uint32 FelExplosionTimer; + uint32 DrainCrystalTimer; + uint32 EmpowerTimer; + + bool IsDraining; + bool DrainingCrystal; + bool Heroic; + uint64 CrystalGUID; // This will help us create a pointer to the crystal we are draining. We store GUIDs, never units in case unit is deleted/offline (offline if player of course). + + void Reset() + { + if(pInstance) + { + //for(uint8 i = 0; i < CRYSTALS_NUMBER; ++i) + for(std::list::iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr) + { + //Unit* pUnit = Unit::GetUnit(*m_creature, FelCrystals[i]); + Unit* pUnit = Unit::GetUnit(*m_creature, *itr); + if(pUnit) + { + if(!pUnit->isAlive()) + ((Creature*)pUnit)->Respawn(); // Let MaNGOS handle setting death state, etc. + + // Only need to set unselectable flag. You can't attack unselectable units so non_attackable flag is not necessary here. + pUnit->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + } + + GameObject* Door = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_SELIN_ENCOUNTER_DOOR)); + if(Door) + Door->SetGoState(0); // Close the door. Open it only in JustDied. + + // Set Inst data for encounter + pInstance->SetData(DATA_SELIN_EVENT, NOT_STARTED); + }else error_log(ERROR_INST_DATA); + + DrainLifeTimer = 3000 + rand()%4000; + DrainManaTimer = DrainLifeTimer + 5000; + FelExplosionTimer = 2100; + DrainCrystalTimer = 10000 + rand()%5000; + DrainCrystalTimer = 20000 + rand()%5000; + EmpowerTimer = 10000; + + IsDraining = false; + DrainingCrystal = false; + CrystalGUID = 0; + } + + void SelectNearestCrystal() + { + if(Crystals.empty()) + return; + + float ShortestDistance = 0; + CrystalGUID = 0; + Unit* pCrystal = NULL; + Unit* CrystalChosen = NULL; + //for(uint8 i = 0; i < CRYSTALS_NUMBER; ++i) + for(std::list::iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr) + { + pCrystal = NULL; + //pCrystal = Unit::GetUnit(*m_creature, FelCrystals[i]); + pCrystal = Unit::GetUnit(*m_creature, *itr); + if(pCrystal && pCrystal->isAlive()) + { + if(!ShortestDistance || (ShortestDistance > m_creature->GetDistance2d(pCrystal))) + { + ShortestDistance = m_creature->GetDistance2d(pCrystal); + CrystalGUID = pCrystal->GetGUID(); + CrystalChosen = pCrystal; // Store a copy of pCrystal so we don't need to recreate a pointer to closest crystal for the movement and yell. + } + } + } + if(CrystalChosen) + { + DoYell(SAY_ENERGY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ENERGY); + CrystalChosen->CastSpell(CrystalChosen, SPELL_FEL_CRYSTAL_COSMETIC, true); + + float x, y, z; // coords that we move to, close to the crystal. + CrystalChosen->GetClosePoint(x, y, z, m_creature->GetObjectSize(), CONTACT_DISTANCE); + + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + m_creature->GetMotionMaster()->MovePoint(1, x, y, z); + DrainingCrystal = true; + } + } + + void ShatterRemainingCrystals() + { + if(Crystals.empty()) + return; + + //for(uint8 i = 0; i < CRYSTALS_NUMBER; ++i) + for(std::list::iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr) + { + //Creature* pCrystal = ((Creature*)Unit::GetUnit(*m_creature, FelCrystals[i])); + Creature* pCrystal = ((Creature*)Unit::GetUnit(*m_creature, *itr)); + if(pCrystal && pCrystal->isAlive()) + pCrystal->DealDamage(pCrystal, pCrystal->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + + void Aggro(Unit* who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL_1); + break; + case 1: + DoYell(SAY_KILL_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL_2); + break; + } + } + + void MovementInform(uint32 type, uint32 id) + { + if(type == POINT_MOTION_TYPE && id == 1) + { + Unit* CrystalChosen = Unit::GetUnit(*m_creature, CrystalGUID); + if(CrystalChosen && CrystalChosen->isAlive()) + { + // Make the crystal attackable + // We also remove NON_ATTACKABLE in case the database has it set. + CrystalChosen->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); + CrystalChosen->CastSpell(m_creature, SPELL_MANA_RAGE, true); + IsDraining = true; + } + else + { + // Make an error message in case something weird happened here + error_log("SD2: Selin Fireheart unable to drain crystal as the crystal is either dead or despawned"); + DrainingCrystal = false; + } + } + } + + void JustDied(Unit* killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if(!pInstance) + { + error_log(ERROR_INST_DATA); + return; + } + + pInstance->SetData(DATA_SELIN_EVENT, DONE); // Encounter complete! + GameObject* EncounterDoor = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_SELIN_ENCOUNTER_DOOR)); + if(EncounterDoor) + EncounterDoor->SetGoState(1); // Open the door + + ShatterRemainingCrystals(); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(!DrainingCrystal) + { + uint32 maxPowerMana = m_creature->GetMaxPower(POWER_MANA); + if( maxPowerMana && ((m_creature->GetPower(POWER_MANA)*100 / maxPowerMana) < 10) ) + { + if( DrainLifeTimer < diff ) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_DRAIN_LIFE); + DrainLifeTimer = 10000; + }else DrainLifeTimer -= diff; + + // Heroic only + if( Heroic ) + { + if( DrainManaTimer < diff ) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_DRAIN_MANA); + DrainManaTimer = 10000; + }else DrainManaTimer -= diff; + } + } + + if( FelExplosionTimer < diff ) + { + if(!m_creature->IsNonMeleeSpellCasted(false)) + { + DoCast(m_creature, SPELL_FEL_EXPLOSION); + FelExplosionTimer = 2000; + } + }else FelExplosionTimer -= diff; + + // If below 10% mana, start recharging + maxPowerMana = m_creature->GetMaxPower(POWER_MANA); + if( maxPowerMana && ((m_creature->GetPower(POWER_MANA)*100 / maxPowerMana) < 10) ) + { + if(DrainCrystalTimer < diff) + { + SelectNearestCrystal(); + if(Heroic) DrainCrystalTimer = 10000 + rand()%5000; + else DrainCrystalTimer = 20000 + rand()%5000; + }else DrainCrystalTimer -= diff; + } + + }else + { + if(IsDraining) + if(EmpowerTimer < diff) + { + IsDraining = false; + DrainingCrystal = false; + DoYell(SAY_EMPOWERED, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_EMPOWERED); + Unit* CrystalChosen = Unit::GetUnit(*m_creature, CrystalGUID); + if(CrystalChosen && CrystalChosen->isAlive()) + // Use Deal Damage to kill it, not setDeathState. + CrystalChosen->DealDamage(CrystalChosen, CrystalChosen->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + CrystalGUID = 0; + + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + }else EmpowerTimer -= diff; + } + + DoMeleeAttackIfReady(); // No need to check if we are draining crystal here, as the spell has a stun. + } +}; + +CreatureAI* GetAI_boss_selin_fireheart(Creature *_Creature) +{ + return new boss_selin_fireheartAI (_Creature); +}; + +struct MANGOS_DLL_DECL mob_fel_crystalAI : public ScriptedAI +{ + mob_fel_crystalAI(Creature *c) : ScriptedAI(c) { Reset(); } + + void Reset() {} + void Aggro(Unit* who) {} + void AttackStart(Unit* who) {} + void MoveInLineOfSight(Unit* who) {} + void UpdateAI(const uint32 diff) {} + + void JustDied(Unit* killer) + { + if(ScriptedInstance* pInstance = ((ScriptedInstance*)m_creature->GetInstanceData())) + { + Creature* Selin = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_SELIN))); + if(Selin && Selin->isAlive()) + { + if(((boss_selin_fireheartAI*)Selin->AI())->CrystalGUID == m_creature->GetGUID()) + { + // Set this to false if we are the creature that Selin is draining so his AI flows properly + ((boss_selin_fireheartAI*)Selin->AI())->DrainingCrystal = false; + ((boss_selin_fireheartAI*)Selin->AI())->IsDraining = false; + ((boss_selin_fireheartAI*)Selin->AI())->EmpowerTimer = 10000; + if(Selin->getVictim()) + { + Selin->AI()->AttackStart(Selin->getVictim()); + Selin->GetMotionMaster()->MoveChase(Selin->getVictim()); + } + } + } + }else error_log(ERROR_INST_DATA); + } +}; + +CreatureAI* GetAI_mob_fel_crystal(Creature *_Creature) +{ + return new mob_fel_crystalAI (_Creature); +}; + +void AddSC_boss_selin_fireheart() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_selin_fireheart"; + newscript->GetAI = GetAI_boss_selin_fireheart; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_fel_crystal"; + newscript->GetAI = GetAI_mob_fel_crystal; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/magisters_terrace/boss_vexallus.cpp b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_vexallus.cpp new file mode 100644 index 00000000000..e815bde5cf2 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/magisters_terrace/boss_vexallus.cpp @@ -0,0 +1,233 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Vexallus +SD%Complete: 90 +SDComment: Heroic and Normal support. Needs further testing. +SDCategory: Magister's Terrace +EndScriptData */ + +#include "precompiled.h" +#include "def_magisters_terrace.h" + +#define SAY_AGGRO "Drain...life..." +#define SOUND_AGGRO 12389 + +#define SAY_ENERGY "Un...con...tainable." +#define SOUND_ENERGY 12392 + +#define SAY_OVERLOAD "Un...leash..." +#define SOUND_OVERLOAD 12390 + +#define SAY_KILL "Con...sume." +#define SOUND_KILL 12393 + +#define SAY_DEATH "What...happen...ed." + +//Pure energy spell info +#define SPELL_ENERGY_BOLT 44342 +#define SPELL_ENERGY_FEEDBACK 44335 + +//Vexallus spell info +#define SPELL_CHAIN_LIGHTNING 44318 +#define SPELL_SUMMON_PURE_ENERGY 44322 //not-working, this script summon this creatures without this spell +#define SPELL_OVERLOAD 44353 +#define SPELL_ARCANE_SHOCK 44319 + +//Creatures +#define CREATURE_PURE_ENERGY 24745 + +struct MANGOS_DLL_DECL boss_vexallusAI : public ScriptedAI +{ + boss_vexallusAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + Heroic = c->GetMap()->IsHeroic() ? true : false; + } + + ScriptedInstance* pInstance; + + uint32 ChainLightningTimer; + uint32 ArcaneShockTimer; + uint32 OverloadTimer; + uint32 SpawnAddInterval; + uint32 AlreadySpawnedAmount; + bool Enraged; + bool Heroic; + + void Reset() + { + ChainLightningTimer = 10000; + ArcaneShockTimer = 8000; + OverloadTimer = 2200; + SpawnAddInterval = 15; + AlreadySpawnedAmount = 0; + + Enraged = false; + + if(pInstance) + pInstance->SetData(DATA_VEXALLUS_EVENT, NOT_STARTED); + } + + void KilledUnit(Unit *victim) + { + DoYell(SAY_KILL, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(victim, SOUND_KILL); + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + if (pInstance) + { + pInstance->SetData(DATA_VEXALLUS_EVENT, DONE); + + GameObject* Door = NULL; + Door = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_VEXALLUS_DOOR)); + if(Door) + Door->SetGoState(0); + } + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + if (pInstance) + pInstance->SetData(DATA_VEXALLUS_EVENT, IN_PROGRESS); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 11) + { + Enraged = true; + } + + if(!Enraged) + { + //used for check, when Vexallus cast adds 85%, 70%, 55%, 40%, 25% + if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < (100-(SpawnAddInterval*(AlreadySpawnedAmount+1)))) + { + DoYell(SAY_ENERGY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ENERGY); + Creature* PureEnergyCreature = NULL; + PureEnergyCreature = DoSpawnCreature(CREATURE_PURE_ENERGY, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (PureEnergyCreature && target) + PureEnergyCreature->AI()->AttackStart(target); + + if(Heroic) // *Heroic mode only - he summons two instead of one. + { + PureEnergyCreature = DoSpawnCreature(CREATURE_PURE_ENERGY, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (PureEnergyCreature && target) + PureEnergyCreature->AI()->AttackStart(target); + } + + ++AlreadySpawnedAmount; + }; + + if(ChainLightningTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + DoCast(target, SPELL_CHAIN_LIGHTNING); + ChainLightningTimer = 10000; + }else ChainLightningTimer -= diff; + + if(ArcaneShockTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + DoCast(target, SPELL_ARCANE_SHOCK); + ArcaneShockTimer = 8000; + }else ArcaneShockTimer -= diff; + }else + { + if(OverloadTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + DoCast(target, SPELL_OVERLOAD); + OverloadTimer = 2200; + }else OverloadTimer -= diff; + } + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_vexallus(Creature *_Creature) +{ + return new boss_vexallusAI (_Creature); +}; + +struct MANGOS_DLL_DECL mob_pure_energyAI : public ScriptedAI +{ + mob_pure_energyAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 EnergyBoltTimer; + + void Reset() + { + EnergyBoltTimer = 1700; + } + + void JustDied(Unit* slayer) + { + slayer->CastSpell(slayer, SPELL_ENERGY_FEEDBACK, true, 0, 0, m_creature->GetGUID()); + } + + void Aggro(Unit *who){} + + void UpdateAI(const uint32 diff) + { + if(!m_creature->getVictim() || !m_creature->SelectHostilTarget()) + return; + + if(EnergyBoltTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ENERGY_BOLT); + EnergyBoltTimer = 1700; + }else EnergyBoltTimer -= diff; + } +}; + +CreatureAI* GetAI_mob_pure_energy(Creature *_Creature) +{ + return new mob_pure_energyAI (_Creature); +}; + +void AddSC_boss_vexallus() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_vexallus"; + newscript->GetAI = GetAI_boss_vexallus; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_pure_energy"; + newscript->GetAI = GetAI_mob_pure_energy; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/magisters_terrace/def_magisters_terrace.h b/src/bindings/scripts/scripts/zone/magisters_terrace/def_magisters_terrace.h new file mode 100644 index 00000000000..d064a50cc13 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/magisters_terrace/def_magisters_terrace.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_MAGISTERS_TERRACE_H +#define DEF_MAGISTERS_TERRACE_H + +#define DATA_SELIN_EVENT 1 +#define DATA_VEXALLUS_EVENT 2 +#define DATA_DELRISSA_EVENT 3 +#define DATA_KAELTHAS_EVENT 4 + +#define DATA_SELIN 5 +#define DATA_FEL_CRYSTAL 6 +#define DATA_FEL_CRYSTAL_SIZE 7 + +#define DATA_VEXALLUS_DOOR 8 +#define DATA_SELIN_DOOR 9 +#define DATA_DELRISSA 10 +#define DATA_DELRISSA_DOOR 11 +#define DATA_SELIN_ENCOUNTER_DOOR 12 + +#define DATA_KAEL_STATUE_LEFT 13 +#define DATA_KAEL_STATUE_RIGHT 14 + +#define DATA_DELRISSA_DEATH_COUNT 15 + +#define ERROR_INST_DATA "SD2 Error: Instance Data not set properly for Magister's Terrace instance (map 585). Encounters will be buggy." +#endif diff --git a/src/bindings/scripts/scripts/zone/magisters_terrace/instance_magisters_terrace.cpp b/src/bindings/scripts/scripts/zone/magisters_terrace/instance_magisters_terrace.cpp new file mode 100644 index 00000000000..39efb41ebbb --- /dev/null +++ b/src/bindings/scripts/scripts/zone/magisters_terrace/instance_magisters_terrace.cpp @@ -0,0 +1,195 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Magisters_Terrace +SD%Complete: 100 +SDComment: Designed only for Selin Fireheart +SDCategory: Magister's Terrace +EndScriptData */ + +#include "precompiled.h" +#include "def_magisters_terrace.h" + +#define NUMBER_OF_ENCOUNTERS 4 + +/* +0 - Selin Fireheart +1 - Vexallus +2 - Priestess Delrissa +3 - Kael'thas Sunstrider +*/ + +struct MANGOS_DLL_DECL instance_magisters_terrace : public ScriptedInstance +{ + instance_magisters_terrace(Map* map) : ScriptedInstance(map) + { + Initialize(); + } + + uint32 Encounters[NUMBER_OF_ENCOUNTERS]; + uint32 DelrissaDeathCount; + + std::list FelCrystals; + std::list::iterator CrystalItr; + + uint64 SelinGUID; + uint64 DelrissaGUID; + uint64 VexallusDoorGUID; + uint64 SelinDoorGUID; + uint64 SelinEncounterDoorGUID; + uint64 DelrissaDoorGUID; + uint64 KaelStatue[2]; + + bool InitializedItr; + + void Initialize() + { + for(uint8 i = 0; i < NUMBER_OF_ENCOUNTERS; i++) + Encounters[i] = NOT_STARTED; + + FelCrystals.clear(); + + DelrissaDeathCount = 0; + + SelinGUID = 0; + DelrissaGUID = 0; + VexallusDoorGUID = 0; + SelinDoorGUID = 0; + SelinEncounterDoorGUID = 0; + DelrissaDoorGUID = 0; + KaelStatue[0] = 0; + KaelStatue[1] = 0; + + InitializedItr = false; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < NUMBER_OF_ENCOUNTERS; i++) + if(Encounters[i] == IN_PROGRESS) + return true; + return false; + } + + uint32 GetData(uint32 identifier) + { + switch(identifier) + { + case DATA_SELIN_EVENT: return Encounters[0]; + case DATA_VEXALLUS_EVENT: return Encounters[1]; + case DATA_DELRISSA_EVENT: return Encounters[2]; + case DATA_KAELTHAS_EVENT: return Encounters[3]; + case DATA_DELRISSA_DEATH_COUNT: return DelrissaDeathCount; + case DATA_FEL_CRYSTAL_SIZE: return FelCrystals.size(); + } + return 0; + } + + void SetData(uint32 identifier, uint32 data) + { + switch(identifier) + { + case DATA_SELIN_EVENT: Encounters[0] = data; break; + case DATA_VEXALLUS_EVENT: Encounters[1] = data; break; + case DATA_DELRISSA_EVENT: Encounters[2] = data; break; + case DATA_KAELTHAS_EVENT: Encounters[3] = data; break; + + case DATA_DELRISSA_DEATH_COUNT: + if(data) ++DelrissaDeathCount; + else DelrissaDeathCount = 0; + } + } + + void OnCreatureCreate(Creature *creature, uint32 entry) + { + switch(entry) + { + case 24723: + SelinGUID = creature->GetGUID(); + break; + case 24560: + DelrissaGUID = creature->GetGUID(); + break; + case 24722: + FelCrystals.push_back(creature->GetGUID()); + break; + } + } + + uint64 GetData64(uint32 identifier) + { + switch(identifier) + { + case DATA_SELIN: return SelinGUID; + case DATA_DELRISSA: return DelrissaGUID; + case DATA_VEXALLUS_DOOR: return VexallusDoorGUID; + case DATA_SELIN_DOOR: return SelinDoorGUID; + case DATA_SELIN_ENCOUNTER_DOOR: return SelinEncounterDoorGUID; + case DATA_DELRISSA_DOOR: return DelrissaDoorGUID; + case DATA_KAEL_STATUE_LEFT: return KaelStatue[0]; + case DATA_KAEL_STATUE_RIGHT: return KaelStatue[1]; + + case DATA_FEL_CRYSTAL: + { + if(FelCrystals.empty()) + { + error_log("SD2: Magisters Terrace: No Fel Crystals loaded in Inst Data"); + return 0; + } + + if(!InitializedItr) + { + CrystalItr = FelCrystals.begin(); + InitializedItr = true; + } + + uint64 guid = *CrystalItr; + ++CrystalItr; + return guid; + } + } + return 0; + } + + void OnObjectCreate(GameObject* go) + { + switch(go->GetEntry()) + { + case 187896: VexallusDoorGUID = go->GetGUID(); break; + case 187979: SelinDoorGUID = go->GetGUID(); break; + case 188118: SelinEncounterDoorGUID = go->GetGUID(); break; + case 187770: DelrissaDoorGUID = go->GetGUID(); break; + case 188165: KaelStatue[0] = go->GetGUID(); break; + case 188166: KaelStatue[1] = go->GetGUID(); break; + } + } +}; + +InstanceData* GetInstanceData_instance_magisters_terrace(Map* map) +{ + return new instance_magisters_terrace(map); +} + +void AddSC_instance_magisters_terrace() +{ + Script *newscript; + + newscript = new Script; + newscript->Name = "instance_magisters_terrace"; + newscript->GetInstanceData = GetInstanceData_instance_magisters_terrace; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/maraudon/boss_celebras_the_cursed.cpp b/src/bindings/scripts/scripts/zone/maraudon/boss_celebras_the_cursed.cpp new file mode 100644 index 00000000000..6f3b92f5e5b --- /dev/null +++ b/src/bindings/scripts/scripts/zone/maraudon/boss_celebras_the_cursed.cpp @@ -0,0 +1,97 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Celebras_the_Cursed +SD%Complete: 100 +SDComment: +SDCategory: Maraudon +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_WRATH 21807 +#define SPELL_ENTANGLINGROOTS 12747 +#define SPELL_CORRUPT_FORCES 21968 + +struct MANGOS_DLL_DECL celebras_the_cursedAI : public ScriptedAI +{ + celebras_the_cursedAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Wrath_Timer; + uint32 EntanglingRoots_Timer; + uint32 CorruptForces_Timer; + + void Reset() + { + Wrath_Timer = 8000; + EntanglingRoots_Timer = 2000; + CorruptForces_Timer = 30000; + } + + void Aggro(Unit *who) { } + + void JustDied(Unit* Killer) + { + m_creature->SummonCreature(13716, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 600000); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Wrath + if (Wrath_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if( target ) + DoCast(target,SPELL_WRATH); + Wrath_Timer = 8000; + }else Wrath_Timer -= diff; + + //EntanglingRoots + if (EntanglingRoots_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ENTANGLINGROOTS); + EntanglingRoots_Timer = 20000; + }else EntanglingRoots_Timer -= diff; + + //CorruptForces + if (CorruptForces_Timer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature,SPELL_CORRUPT_FORCES); + CorruptForces_Timer = 20000; + }else CorruptForces_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_celebras_the_cursed(Creature *_Creature) +{ + return new celebras_the_cursedAI (_Creature); +} + +void AddSC_boss_celebras_the_cursed() +{ + Script *newscript; + newscript = new Script; + newscript->Name="celebras_the_cursed"; + newscript->GetAI = GetAI_celebras_the_cursed; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/maraudon/boss_landslide.cpp b/src/bindings/scripts/scripts/zone/maraudon/boss_landslide.cpp new file mode 100644 index 00000000000..466f35dbba7 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/maraudon/boss_landslide.cpp @@ -0,0 +1,94 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Landslide +SD%Complete: 100 +SDComment: +SDCategory: Maraudon +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_KNOCKAWAY 18670 +#define SPELL_TRAMPLE 5568 +#define SPELL_LANDSLIDE 21808 + +struct MANGOS_DLL_DECL boss_landslideAI : public ScriptedAI +{ + boss_landslideAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 KnockAway_Timer; + uint32 Trample_Timer; + uint32 Landslide_Timer; + + void Reset() + { + KnockAway_Timer = 8000; + Trample_Timer = 2000; + Landslide_Timer = 0; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //KnockAway_Timer + if (KnockAway_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCKAWAY); + KnockAway_Timer = 15000; + }else KnockAway_Timer -= diff; + + //Trample_Timer + if (Trample_Timer < diff) + { + DoCast(m_creature,SPELL_TRAMPLE); + Trample_Timer = 8000; + }else Trample_Timer -= diff; + + //Landslide + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 50 ) + { + if (Landslide_Timer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature,SPELL_LANDSLIDE); + Landslide_Timer = 60000; + } else Landslide_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_landslide(Creature *_Creature) +{ + return new boss_landslideAI (_Creature); +} + +void AddSC_boss_landslide() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_landslide"; + newscript->GetAI = GetAI_boss_landslide; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/maraudon/boss_noxxion.cpp b/src/bindings/scripts/scripts/zone/maraudon/boss_noxxion.cpp new file mode 100644 index 00000000000..b7bd6cc62fb --- /dev/null +++ b/src/bindings/scripts/scripts/zone/maraudon/boss_noxxion.cpp @@ -0,0 +1,149 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Noxxion +SD%Complete: 100 +SDComment: +SDCategory: Maraudon +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_TOXICVOLLEY 21687 +#define SPELL_UPPERCUT 22916 + +struct MANGOS_DLL_DECL boss_noxxionAI : public ScriptedAI +{ + boss_noxxionAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ToxicVolley_Timer; + uint32 Uppercut_Timer; + uint32 Adds_Timer; + uint32 Invisible_Timer; + bool Invisible; + int Rand; + int RandX; + int RandY; + Creature* Summoned; + + void Reset() + { + ToxicVolley_Timer = 7000; + Uppercut_Timer = 16000; + Adds_Timer = 19000; + Invisible_Timer = 15000; //Too much too low? + Invisible = false; + } + + void Aggro(Unit *who) + { + } + + void SummonAdds(Unit* victim) + { + Rand = rand()%8; + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = rand()%8; + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + Summoned = DoSpawnCreature(13456, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 90000); + if(Summoned) + ((CreatureAI*)Summoned->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + if (Invisible && Invisible_Timer < diff) + { + //Become visible again + m_creature->setFaction(14); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + //Noxxion model + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,11172); + Invisible = false; + //m_creature->m_canMove = true; + } else if (Invisible) + { + Invisible_Timer -= diff; + //Do nothing while invisible + return; + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //ToxicVolley_Timer + if (ToxicVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_TOXICVOLLEY); + ToxicVolley_Timer = 9000; + }else ToxicVolley_Timer -= diff; + + //Uppercut_Timer + if (Uppercut_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_UPPERCUT); + Uppercut_Timer = 12000; + }else Uppercut_Timer -= diff; + + //Adds_Timer + if (!Invisible && Adds_Timer < diff) + { + //Inturrupt any spell casting + //m_creature->m_canMove = true; + m_creature->InterruptNonMeleeSpells(false); + m_creature->setFaction(35); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + // Invisible Model + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,11686); + SummonAdds(m_creature->getVictim()); + SummonAdds(m_creature->getVictim()); + SummonAdds(m_creature->getVictim()); + SummonAdds(m_creature->getVictim()); + SummonAdds(m_creature->getVictim()); + Invisible = true; + Invisible_Timer = 15000; + + Adds_Timer = 40000; + }else Adds_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_noxxion(Creature *_Creature) +{ + return new boss_noxxionAI (_Creature); +} + +void AddSC_boss_noxxion() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_noxxion"; + newscript->GetAI = GetAI_boss_noxxion; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/maraudon/boss_princess_theradras.cpp b/src/bindings/scripts/scripts/zone/maraudon/boss_princess_theradras.cpp new file mode 100644 index 00000000000..64215234ee5 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/maraudon/boss_princess_theradras.cpp @@ -0,0 +1,108 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Princess_Theradras +SD%Complete: 100 +SDComment: +SDCategory: Maraudon +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_DUSTFIELD 21909 +#define SPELL_BOULDER 21832 +#define SPELL_THRASH 3391 +#define SPELL_REPULSIVEGAZE 21869 + +struct MANGOS_DLL_DECL boss_ptheradrasAI : public ScriptedAI +{ + boss_ptheradrasAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Dustfield_Timer; + uint32 Boulder_Timer; + uint32 Thrash_Timer; + uint32 RepulsiveGaze_Timer; + + void Reset() + { + Dustfield_Timer = 8000; + Boulder_Timer = 2000; + Thrash_Timer = 5000; + RepulsiveGaze_Timer = 23000; + } + + void Aggro(Unit *who) + { + } + + void JustDied(Unit* Killer) + { + m_creature->SummonCreature(12238,28.067,61.875,-123.405,4.67,TEMPSUMMON_TIMED_DESPAWN,600000); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Dustfield_Timer + if (Dustfield_Timer < diff) + { + DoCast(m_creature,SPELL_DUSTFIELD); + Dustfield_Timer = 14000; + }else Dustfield_Timer -= diff; + + //Boulder_Timer + if (Boulder_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if( target ) + DoCast(target,SPELL_BOULDER); + Boulder_Timer = 10000; + }else Boulder_Timer -= diff; + + //RepulsiveGaze_Timer + if (RepulsiveGaze_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_REPULSIVEGAZE); + RepulsiveGaze_Timer = 20000; + }else RepulsiveGaze_Timer -= diff; + + //Thrash_Timer + if (Thrash_Timer < diff) + { + DoCast(m_creature,SPELL_THRASH); + Thrash_Timer = 18000; + }else Thrash_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_ptheradras(Creature *_Creature) +{ + return new boss_ptheradrasAI (_Creature); +} + +void AddSC_boss_ptheradras() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_princess_theradras"; + newscript->GetAI = GetAI_boss_ptheradras; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/boss_baron_geddon.cpp b/src/bindings/scripts/scripts/zone/molten_core/boss_baron_geddon.cpp new file mode 100644 index 00000000000..075ec15da19 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/boss_baron_geddon.cpp @@ -0,0 +1,105 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Baron_Geddon +SD%Complete: 100 +SDComment: +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_INFERNO 19695 +#define SPELL_IGNITEMANA 19659 +#define SPELL_LIVINGBOMB 20475 +#define SPELL_ARMAGEDDOM 20479 + +struct MANGOS_DLL_DECL boss_baron_geddonAI : public ScriptedAI +{ + boss_baron_geddonAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Inferno_Timer; + uint32 IgniteMana_Timer; + uint32 LivingBomb_Timer; + + void Reset() + { + Inferno_Timer = 45000; //These times are probably wrong + IgniteMana_Timer = 30000; + LivingBomb_Timer = 35000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are <2% hp cast Armageddom + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 2 && !m_creature->IsNonMeleeSpellCasted(false)) + { + DoCast(m_creature,SPELL_ARMAGEDDOM); + DoTextEmote("performs one last service for Ragnaros.",NULL); + return; + } + + //Inferno_Timer + if (Inferno_Timer < diff) + { + DoCast(m_creature,SPELL_INFERNO); + Inferno_Timer = 45000; + }else Inferno_Timer -= diff; + + //IgniteMana_Timer + if (IgniteMana_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_IGNITEMANA); + + IgniteMana_Timer = 30000; + }else IgniteMana_Timer -= diff; + + //LivingBomb_Timer + if (LivingBomb_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_LIVINGBOMB); + + LivingBomb_Timer = 35000; + }else LivingBomb_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_baron_geddon(Creature *_Creature) +{ + return new boss_baron_geddonAI (_Creature); +} + +void AddSC_boss_baron_geddon() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_baron_geddon"; + newscript->GetAI = GetAI_boss_baron_geddon; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/boss_garr.cpp b/src/bindings/scripts/scripts/zone/molten_core/boss_garr.cpp new file mode 100644 index 00000000000..82696672654 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/boss_garr.cpp @@ -0,0 +1,168 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Garr +SD%Complete: 50 +SDComment: Adds NYI +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" + +// Garr spells +#define SPELL_ANTIMAGICPULSE 19492 +#define SPELL_MAGMASHACKLES 19496 +#define SPELL_ENRAGE 19516 //Stacking enrage (stacks to 10 times) + +//Add spells +#define SPELL_ERUPTION 19497 +#define SPELL_IMMOLATE 20294 + +struct MANGOS_DLL_DECL boss_garrAI : public ScriptedAI +{ + boss_garrAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 AntiMagicPulse_Timer; + uint32 MagmaShackles_Timer; + uint32 CheckAdds_Timer; + uint64 Add[8]; + bool Enraged[8]; + + void Reset() + { + AntiMagicPulse_Timer = 25000; //These times are probably wrong + MagmaShackles_Timer = 15000; + CheckAdds_Timer = 2000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //AntiMagicPulse_Timer + if (AntiMagicPulse_Timer < diff) + { + DoCast(m_creature,SPELL_ANTIMAGICPULSE); + AntiMagicPulse_Timer = 10000 + rand()%5000; + }else AntiMagicPulse_Timer -= diff; + + //MagmaShackles_Timer + if (MagmaShackles_Timer < diff) + { + DoCast(m_creature,SPELL_MAGMASHACKLES); + MagmaShackles_Timer = 8000 + rand()%4000; + }else MagmaShackles_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_fireswornAI : public ScriptedAI +{ + mob_fireswornAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Immolate_Timer; + + void Reset() + { + Immolate_Timer = 4000; //These times are probably wrong + + //m_creature->RemoveAllAuras(); + //m_creature->DeleteThreatList(); + //m_creature->CombatStop(); + //DoGoHome(); + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Immolate_Timer + if (Immolate_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + DoCast(target,SPELL_IMMOLATE); + + Immolate_Timer = 5000 + rand()%5000; + }else Immolate_Timer -= diff; + + //Cast Erruption and let them die + if (m_creature->GetHealth() <= m_creature->GetMaxHealth() * 0.10) + { + DoCast(m_creature->getVictim(),SPELL_ERUPTION); + m_creature->setDeathState(JUST_DIED); + m_creature->RemoveCorpse(); + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_garr(Creature *_Creature) +{ + return new boss_garrAI (_Creature); +} + +CreatureAI* GetAI_mob_firesworn(Creature *_Creature) +{ + return new mob_fireswornAI (_Creature); +} + +void AddSC_boss_garr() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_garr"; + newscript->GetAI = GetAI_boss_garr; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_firesworn"; + newscript->GetAI = GetAI_mob_firesworn; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/boss_gehennas.cpp b/src/bindings/scripts/scripts/zone/molten_core/boss_gehennas.cpp new file mode 100644 index 00000000000..96c02e77ea1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/boss_gehennas.cpp @@ -0,0 +1,91 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Gehennas +SD%Complete: 90 +SDComment: Adds MC NYI +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWBOLT 19728 +#define SPELL_RAINOFFIRE 19717 +#define SPELL_GEHENNASCURSE 19716 + +struct MANGOS_DLL_DECL boss_gehennasAI : public ScriptedAI +{ + boss_gehennasAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowBolt_Timer; + uint32 RainOfFire_Timer; + uint32 GehennasCurse_Timer; + + void Reset() + { + ShadowBolt_Timer = 6000; + RainOfFire_Timer = 10000; + GehennasCurse_Timer = 12000; + } + + void Aggro(Unit *who) { } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //ShadowBolt_Timer + if (ShadowBolt_Timer < diff) + { + if( Unit* bTarget = SelectUnit(SELECT_TARGET_RANDOM,1) ) + DoCast(bTarget,SPELL_SHADOWBOLT); + ShadowBolt_Timer = 7000; + }else ShadowBolt_Timer -= diff; + + //RainOfFire_Timer + if (RainOfFire_Timer < diff) + { + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_RAINOFFIRE); + + RainOfFire_Timer = 4000 + rand()%8000; + }else RainOfFire_Timer -= diff; + + //GehennasCurse_Timer + if (GehennasCurse_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_GEHENNASCURSE); + GehennasCurse_Timer = 22000 + rand()%8000; + }else GehennasCurse_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_gehennas(Creature *_Creature) +{ + return new boss_gehennasAI (_Creature); +} + +void AddSC_boss_gehennas() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_gehennas"; + newscript->GetAI = GetAI_boss_gehennas; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/boss_golemagg.cpp b/src/bindings/scripts/scripts/zone/molten_core/boss_golemagg.cpp new file mode 100644 index 00000000000..08bb85201ca --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/boss_golemagg.cpp @@ -0,0 +1,219 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Golemagg +SD%Complete: 90 +SDComment: +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "def_molten_core.h" + +#define SPELL_MAGMASPLASH 13879 +#define SPELL_PYROBLAST 20228 +#define SPELL_EARTHQUAKE 19798 +#define SPELL_ENRAGE 19953 +#define SPELL_BUFF 20553 + +//-- CoreRager Spells -- +#define SPELL_MANGLE 19820 +#define SPELL_AEGIS 20620 //This is self casted whenever we are below 50% + +#define EMOTE_AEGIS "refuses to die while its master is in trouble" + +struct MANGOS_DLL_DECL boss_golemaggAI : public ScriptedAI +{ + boss_golemaggAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Reset(); + } + + uint32 Pyroblast_Timer; + uint32 EarthQuake_Timer; + uint32 Enrage_Timer; + uint32 Buff_Timer; + ScriptedInstance *pInstance; + + void Reset() + { + Pyroblast_Timer = 7000; //These times are probably wrong + EarthQuake_Timer = 3000; + Buff_Timer = 2500; + Enrage_Timer = 0; + + m_creature->CastSpell(m_creature,SPELL_MAGMASPLASH,true); + } + + void Aggro(Unit *who) + { + } + + void JustDied(Unit* Killer) + { + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + if(pInstance) + pInstance->SetData(DATA_GOLEMAGG_DEATH, 0); + } + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Pyroblast_Timer + if (Pyroblast_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_PYROBLAST); + + Pyroblast_Timer = 7000; + }else Pyroblast_Timer -= diff; + + //Enrage_Timer + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 11 ) + { + if (Enrage_Timer < diff) + { + DoCast(m_creature,SPELL_ENRAGE); + Enrage_Timer = 62000; + }else Enrage_Timer -= diff; + } + + //EarthQuake_Timer + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 11 ) + { + if (EarthQuake_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_EARTHQUAKE); + EarthQuake_Timer = 3000; + }else EarthQuake_Timer -= diff; + } + + //Casting Buff for Coreragers. Spell is not working right. Players get the buff... + // if(Buff_Timer < diff) + // { + // DoCast(m_creature, SPELL_BUFF); + // Buff_Timer = 2500; + // }else Buff_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_core_ragerAI : public ScriptedAI +{ + mob_core_ragerAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Reset(); + } + + uint32 Mangle_Timer; + uint32 Check_Timer; + ScriptedInstance *pInstance; + + void Reset() + { + Mangle_Timer = 7000; //These times are probably wrong + Check_Timer = 1000; + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Mangle_Timer + if (Mangle_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MANGLE); + Mangle_Timer = 10000; + }else Mangle_Timer -= diff; + + //Cast AEGIS + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 50 ) + { + DoCast(m_creature,SPELL_AEGIS); + DoTextEmote(EMOTE_AEGIS, NULL); + } + + //Check_Timer + if(Check_Timer < diff) + { + if(pInstance) + { + Unit *pGolemagg = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_GOLEMAGG)); + if(!pGolemagg || !pGolemagg->isAlive()) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, true); + } + } + + Check_Timer = 1000; + }else Check_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_golemagg(Creature *_Creature) +{ + return new boss_golemaggAI (_Creature); +} + +CreatureAI* GetAI_mob_core_rager(Creature *_Creature) +{ + return new mob_core_ragerAI (_Creature); +} + +void AddSC_boss_golemagg() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_golemagg"; + newscript->GetAI = GetAI_boss_golemagg; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_core_rager"; + newscript->GetAI = GetAI_mob_core_rager; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/boss_lucifron.cpp b/src/bindings/scripts/scripts/zone/molten_core/boss_lucifron.cpp new file mode 100644 index 00000000000..c0083fe1a7f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/boss_lucifron.cpp @@ -0,0 +1,90 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Lucifron +SD%Complete: 100 +SDComment: +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_IMPENDINGDOOM 19702 +#define SPELL_LUCIFRONCURSE 19703 +#define SPELL_SHADOWSHOCK 20603 + +struct MANGOS_DLL_DECL boss_lucifronAI : public ScriptedAI +{ + boss_lucifronAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ImpendingDoom_Timer; + uint32 LucifronCurse_Timer; + uint32 ShadowShock_Timer; + + void Reset() + { + ImpendingDoom_Timer = 10000; //Initial cast after 10 seconds so the debuffs alternate + LucifronCurse_Timer = 20000; //Initial cast after 20 seconds + ShadowShock_Timer = 6000; //6 seconds + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Impending doom timer + if (ImpendingDoom_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_IMPENDINGDOOM); + ImpendingDoom_Timer = 20000; + }else ImpendingDoom_Timer -= diff; + + //Lucifron's curse timer + if (LucifronCurse_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_LUCIFRONCURSE); + LucifronCurse_Timer = 15000; + }else LucifronCurse_Timer -= diff; + + //Shadowshock + if (ShadowShock_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWSHOCK); + ShadowShock_Timer = 6000; + }else ShadowShock_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_lucifron(Creature *_Creature) +{ + return new boss_lucifronAI (_Creature); +} + +void AddSC_boss_lucifron() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_lucifron"; + newscript->GetAI = GetAI_boss_lucifron; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/boss_magmadar.cpp b/src/bindings/scripts/scripts/zone/molten_core/boss_magmadar.cpp new file mode 100644 index 00000000000..1d2d9fa1617 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/boss_magmadar.cpp @@ -0,0 +1,97 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Magmadar +SD%Complete: 75 +SDComment: Conflag on ground nyi, fear causes issues without VMAPs +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FRENZY 19451 +#define SPELL_MAGMASPIT 19449 //This is actually a buff he gives himself +#define SPELL_PANIC 19408 +#define SPELL_LAVABOMB 19411 //This calls a dummy server side effect that isn't implemented yet +#define SPELL_LAVABOMB_ALT 19428 //This is the spell that the lava bomb casts + +struct MANGOS_DLL_DECL boss_magmadarAI : public ScriptedAI +{ + boss_magmadarAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Frenzy_Timer; + uint32 Panic_Timer; + uint32 Lavabomb_Timer; + + void Reset() + { + Frenzy_Timer = 30000; + Panic_Timer = 20000; + Lavabomb_Timer = 12000; + + m_creature->CastSpell(m_creature,SPELL_MAGMASPIT,true); + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Frenzy_Timer + if (Frenzy_Timer < diff) + { + DoTextEmote("goes into a killing frenzy!",NULL); + DoCast(m_creature,SPELL_FRENZY); + Frenzy_Timer = 15000; + }else Frenzy_Timer -= diff; + + //Panic_Timer + if (Panic_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_PANIC); + Panic_Timer = 35000; + }else Panic_Timer -= diff; + + //Lavabomb_Timer + if (Lavabomb_Timer < diff) + { + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_LAVABOMB_ALT); + + Lavabomb_Timer = 12000; + }else Lavabomb_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_magmadar(Creature *_Creature) +{ + return new boss_magmadarAI (_Creature); +} + +void AddSC_boss_magmadar() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_magmadar"; + newscript->GetAI = GetAI_boss_magmadar; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/boss_majordomo_executus.cpp b/src/bindings/scripts/scripts/zone/molten_core/boss_majordomo_executus.cpp new file mode 100644 index 00000000000..8c7129eb691 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/boss_majordomo_executus.cpp @@ -0,0 +1,133 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_MajorDomo_Executus +SD%Complete: 30 +SDComment: Correct spawning and Event NYI +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_MAGICREFLECTION 20619 +#define SPELL_DAMAGEREFLECTION 21075 +#define SPELL_BLASTWAVE 20229 +#define SPELL_AEGIS 20620 //This is self casted whenever we are below 50% + +#define SAY_AGGRO "Reckless mortals, none may challenge the sons of the living flame!" +#define SOUND_AGGRO 8035 + +#define SAY_SPAWN "The runes of warding have been destroyed! Hunt down the infedels my bretheren." +#define SOUND_SPAWN 8039 + +#define SAY_DEFEAT "Impossible! Stay your attack mortals! I submitt! I submitt! Brashly you have come to rest the secrets of the living flame. You will soon regret the recklessness of your quest. I go now to summon the lord whos house this is. Should you seek an audiance with him your paltry lives will surly be forfit. Nevertheless seek out his lair if you dare!" +#define SOUND_DEFEAT 8038 + +#define SAY_KILL "Ashes to Ashes!" +#define SOUND_KILL 8037 + +#define SAY_SPECIAL "Burn mortals! Burn for this transgression!" +#define SOUND_SPECIAL 8036 + +#define SAY_SUMMON "Behold Ragnaros, the Firelord! He who was ancient when this world was young! Bow before him, mortals! Bow before your ending!" +#define SOUND_SUMMON 8040 + +#define SAY_ARRIVAL1 "These mortal infidels, my lord! They have invaded your sanctum, and seek to steal your secrets!" +#define SOUND_ARRIVAL1 8041 + +struct MANGOS_DLL_DECL boss_majordomoAI : public ScriptedAI +{ + boss_majordomoAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 MagicReflection_Timer; + uint32 DamageReflection_Timer; + uint32 Blastwave_Timer; + + void Reset() + { + MagicReflection_Timer = 30000; //Damage reflection first so we alternate + DamageReflection_Timer = 15000; + Blastwave_Timer = 10000; + } + + void KilledUnit(Unit* victim) + { + if (rand()%5) + return; + + DoYell(SAY_KILL,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Cast Ageis if less than 50% hp + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 50) + { + DoCast(m_creature,SPELL_AEGIS); + } + + //MagicReflection_Timer + // if (MagicReflection_Timer < diff) + // { + // DoCast(m_creature, SPELL_MAGICREFLECTION); + + //60 seconds until we should cast this agian + // MagicReflection_Timer = 30000; + // }else MagicReflection_Timer -= diff; + + //DamageReflection_Timer + // if (DamageReflection_Timer < diff) + // { + // DoCast(m_creature, SPELL_DAMAGEREFLECTION); + + //60 seconds until we should cast this agian + // DamageReflection_Timer = 30000; + // }else DamageReflection_Timer -= diff; + + //Blastwave_Timer + if (Blastwave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_BLASTWAVE); + Blastwave_Timer = 10000; + }else Blastwave_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_majordomo(Creature *_Creature) +{ + return new boss_majordomoAI (_Creature); +} + +void AddSC_boss_majordomo() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_majordomo"; + newscript->GetAI = GetAI_boss_majordomo; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/boss_ragnaros.cpp b/src/bindings/scripts/scripts/zone/molten_core/boss_ragnaros.cpp new file mode 100644 index 00000000000..3a043987cca --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/boss_ragnaros.cpp @@ -0,0 +1,317 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Ragnaros +SD%Complete: 75 +SDComment: Intro Dialog and event NYI +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_HANDOFRAGNAROS 19780 +#define SPELL_WRATHOFRAGNAROS 20566 +#define SPELL_LAVABURST 21158 + +#define SPELL_MAGMABURST 20565 //Ranged attack + +#define SPELL_SONSOFFLAME_DUMMY 21108 //Server side effect +#define SPELL_RAGSUBMERGE 21107 //Stealth aura +#define SPELL_RAGEMERGE 20568 +#define SPELL_MELTWEAPON 21388 +#define SPELL_ELEMENTALFIRE 20564 +#define SPELL_ERRUPTION 17731 + +#define SAY_ARRIVAL_1 "TOO SOON! YOU HAVE AWAKENED ME TOO SOON, EXECUTUS! WHAT IS THE MEANING OF THIS INTRUSION?" +#define SAY_ARRIVAL_3 "FOOL! YOU ALLOWED THESE INSECTS TO RUN RAMPANT THROUGH THE HALLOWED CORE, AND NOW YOU LEAD THEM TO MY VERY LAIR? YOU HAVE FAILED ME, EXECUTUS! JUSTICE SHALL BE MET, INDEED!" +#define SAY_ARRIVAL_5 "NOW FOR YOU, INSECTS. BOLDLY YOU SAUGHT THE POWER OF RAGNAROS NOW YOU SHALL SEE IT FIRST HAND." +#define SAY_REINFORCEMENTS1 "COME FORTH, MY SERVANTS! DEFEND YOUR MASTER!" +#define SAY_REINFORCEMENTS2 "YOU CANNOT DEFEAT THE LIVING FLAME! COME YOU MINIONS OF FIRE! COME FORTH YOU CREATURES OF HATE! YOUR MASTER CALLS!" +#define SAY_HAND "BY FIRE BE PURGED!" +#define SAY_WRATH "TASTE THE FLAMES OF SULFURON!" +#define SAY_KILL "DIE INSECT!" +#define SAY_MAGMABURST "MY PATIENCE IS DWINDILING! COME NATS TO YOUR DEATH!" + +#define SOUND_ARRIVAL_1 8043 +#define SOUND_ARRIVAL_3 8044 +#define SOUND_ARRIVAL_5 8045 +#define SOUND_REINFORCEMENTS1 8049 +#define SOUND_REINFORCEMENTS2 8050 +#define SOUND_HAND 8046 +#define SOUND_WRATH 8047 +#define SOUND_KILL 8051 +#define SOUND_MAGMABURST 8048 + +#define ADD_1X 848.740356 +#define ADD_1Y -816.103455 +#define ADD_1Z -229.74327 +#define ADD_1O 2.615287 + +#define ADD_2X 852.560791 +#define ADD_2Y -849.861511 +#define ADD_2Z -228.560974 +#define ADD_2O 2.836073 + +#define ADD_3X 808.710632 +#define ADD_3Y -852.845764 +#define ADD_3Z -227.914963 +#define ADD_3O 0.964207 + +#define ADD_4X 786.597107 +#define ADD_4Y -821.132874 +#define ADD_4Z -226.350128 +#define ADD_4O 0.949377 + +#define ADD_5X 796.219116 +#define ADD_5Y -800.948059 +#define ADD_5Z -226.010361 +#define ADD_5O 0.560603 + +#define ADD_6X 821.602539 +#define ADD_6Y -782.744109 +#define ADD_6Z -226.023575 +#define ADD_6O 6.157440 + +#define ADD_7X 844.924744 +#define ADD_7Y -769.453735 +#define ADD_7Z -225.521698 +#define ADD_7O 4.4539958 + +#define ADD_8X 839.823364 +#define ADD_8Y -810.869385 +#define ADD_8Z -229.683182 +#define ADD_8O 4.693108 + +struct MANGOS_DLL_DECL boss_ragnarosAI : public Scripted_NoMovementAI +{ + boss_ragnarosAI(Creature *c) : Scripted_NoMovementAI(c) {Reset();} + + uint32 WrathOfRagnaros_Timer; + uint32 HandOfRagnaros_Timer; + uint32 LavaBurst_Timer; + uint32 MagmaBurst_Timer; + uint32 ElementalFire_Timer; + uint32 Erruption_Timer; + uint32 Submerge_Timer; + uint32 Attack_Timer; + Creature *Summoned; + bool HasYelledMagmaBurst; + bool HasSubmergedOnce; + bool WasBanished; + bool HasAura; + + void Reset() + { + WrathOfRagnaros_Timer = 30000; + HandOfRagnaros_Timer = 25000; + LavaBurst_Timer = 10000; + MagmaBurst_Timer = 2000; + Erruption_Timer = 15000; + ElementalFire_Timer = 3000; + Submerge_Timer = 180000; + Attack_Timer = 90000; + HasYelledMagmaBurst = false; + HasSubmergedOnce = false; + WasBanished = false; + + m_creature->CastSpell(m_creature,SPELL_MELTWEAPON,true); + HasAura = true; + } + + void KilledUnit(Unit* victim) + { + if (rand()%5) + return; + + DoYell(SAY_KILL, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL); + } + + void Aggro(Unit *who) + { + DoYell(SAY_ARRIVAL_5,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_ARRIVAL_5); + } + + void UpdateAI(const uint32 diff) + { + if (WasBanished && Attack_Timer < diff) + { + //Become unbanished again + m_creature->setFaction(14); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DoCast(m_creature,SPELL_RAGEMERGE); + WasBanished = false; + } else if (WasBanished) + { + Attack_Timer -= diff; + //Do nothing while banished + return; + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //WrathOfRagnaros_Timer + if (WrathOfRagnaros_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_WRATHOFRAGNAROS); + + if (rand()%2 == 0) + { + DoYell(SAY_WRATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_WRATH); + } + + WrathOfRagnaros_Timer = 30000; + }else WrathOfRagnaros_Timer -= diff; + + //HandOfRagnaros_Timer + if (HandOfRagnaros_Timer < diff) + { + DoCast(m_creature,SPELL_HANDOFRAGNAROS); + + if (rand()%2==0) + { + DoYell(SAY_HAND,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_HAND); + } + + HandOfRagnaros_Timer = 25000; + }else HandOfRagnaros_Timer -= diff; + + //LavaBurst_Timer + if (LavaBurst_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_LAVABURST); + LavaBurst_Timer = 10000; + }else LavaBurst_Timer -= diff; + + //Erruption_Timer + if (LavaBurst_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ERRUPTION); + Erruption_Timer = 20000 + rand()%25000; + }else Erruption_Timer -= diff; + + //ElementalFire_Timer + if (ElementalFire_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ELEMENTALFIRE); + ElementalFire_Timer = 10000 + rand()%4000; + }else ElementalFire_Timer -= diff; + + //Submerge_Timer + if (!WasBanished && Submerge_Timer < diff) + { + //Creature spawning and ragnaros becomming unattackable + //is not very well supported in the core + //so added normaly spawning and banish workaround and attack again after 90 secs. + + m_creature->InterruptNonMeleeSpells(false); + //Root self + DoCast(m_creature,23973); + m_creature->setFaction(35); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_SUBMERGE); + + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + if (!HasSubmergedOnce) + { + DoYell(SAY_REINFORCEMENTS1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_REINFORCEMENTS1); + + // summon 10 elementals + Unit* target = NULL; + for(int i = 0; i < 9;i++) + { + target = SelectUnit(SELECT_TARGET_RANDOM,0); + Summoned = m_creature->SummonCreature(12143,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,900000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + } + + HasSubmergedOnce = true; + WasBanished = true; + DoCast(m_creature,SPELL_RAGSUBMERGE); + Attack_Timer = 90000; + + }else + { + DoYell(SAY_REINFORCEMENTS2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_REINFORCEMENTS2); + + Unit* target = NULL; + for(int i = 0; i < 9;i++) + { + target = SelectUnit(SELECT_TARGET_RANDOM,0); + Summoned = m_creature->SummonCreature(12143,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,900000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + } + + WasBanished = true; + DoCast(m_creature,SPELL_RAGSUBMERGE); + Attack_Timer = 90000; + } + + Submerge_Timer = 180000; + }else Submerge_Timer -= diff; + + //If we are within range melee the target + if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + { + //Make sure our attack is ready and we arn't currently casting + if( m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false)) + { + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); + } + }else + { + //MagmaBurst_Timer + if (MagmaBurst_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MAGMABURST); + + if (!HasYelledMagmaBurst) + { + //Say our dialog + DoYell(SAY_MAGMABURST,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_MAGMABURST); + HasYelledMagmaBurst = true; + } + + MagmaBurst_Timer = 2500; + }else MagmaBurst_Timer -= diff; + } + } +}; +CreatureAI* GetAI_boss_ragnaros(Creature *_Creature) +{ + return new boss_ragnarosAI (_Creature); +} + +void AddSC_boss_ragnaros() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_ragnaros"; + newscript->GetAI = GetAI_boss_ragnaros; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/boss_shazzrah.cpp b/src/bindings/scripts/scripts/zone/molten_core/boss_shazzrah.cpp new file mode 100644 index 00000000000..5c8746b2567 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/boss_shazzrah.cpp @@ -0,0 +1,121 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Shazzrah +SD%Complete: 75 +SDComment: Teleport NYI +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_ARCANEEXPLOSION 19712 +#define SPELL_SHAZZRAHCURSE 19713 +#define SPELL_DEADENMAGIC 19714 +#define SPELL_COUNTERSPELL 19715 + +struct MANGOS_DLL_DECL boss_shazzrahAI : public ScriptedAI +{ + boss_shazzrahAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ArcaneExplosion_Timer; + uint32 ShazzrahCurse_Timer; + uint32 DeadenMagic_Timer; + uint32 Countspell_Timer; + uint32 Blink_Timer; + + void Reset() + { + ArcaneExplosion_Timer = 6000; //These times are probably wrong + ShazzrahCurse_Timer = 10000; + DeadenMagic_Timer = 24000; + Countspell_Timer = 15000; + Blink_Timer = 30000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //ArcaneExplosion_Timer + if (ArcaneExplosion_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ARCANEEXPLOSION); + ArcaneExplosion_Timer = 5000 + rand()%4000; + }else ArcaneExplosion_Timer -= diff; + + //ShazzrahCurse_Timer + if (ShazzrahCurse_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_SHAZZRAHCURSE); + + ShazzrahCurse_Timer = 25000 + rand()%5000; + }else ShazzrahCurse_Timer -= diff; + + //DeadenMagic_Timer + if (DeadenMagic_Timer < diff) + { + DoCast(m_creature,SPELL_DEADENMAGIC); + DeadenMagic_Timer = 35000; + }else DeadenMagic_Timer -= diff; + + //Countspell_Timer + if (Countspell_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_COUNTERSPELL); + Countspell_Timer = 16000 + rand()%4000; + }else Countspell_Timer -= diff; + + //Blink_Timer + if (Blink_Timer < diff) + { + // Teleporting him to a random gamer and casting Arcane Explosion after that. + // Blink is not working cause of LoS System we need to do this hardcoded. + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + m_creature->Relocate(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0); + m_creature->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0,true,0); + DoCast(target,SPELL_ARCANEEXPLOSION); + DoResetThreat(); + + Blink_Timer = 45000; + }else Blink_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_shazzrah(Creature *_Creature) +{ + return new boss_shazzrahAI (_Creature); +} + +void AddSC_boss_shazzrah() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_shazzrah"; + newscript->GetAI = GetAI_boss_shazzrah; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/boss_sulfuron_harbinger.cpp b/src/bindings/scripts/scripts/zone/molten_core/boss_sulfuron_harbinger.cpp new file mode 100644 index 00000000000..fd4affaa570 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/boss_sulfuron_harbinger.cpp @@ -0,0 +1,215 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Sulfuron_Harbringer +SD%Complete: 80 +SDComment: Adds NYI +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "def_molten_core.h" + +#define SPELL_DARKSTRIKE 19777 +#define SPELL_DEMORALIZINGSHOUT 19778 +#define SPELL_INSPIRE 19779 +#define SPELL_KNOCKDOWN 19780 +#define SPELL_FLAMESPEAR 19781 + +//Adds Spells +#define SPELL_HEAL 19775 +#define SPELL_SHADOWWORDPAIN 19776 +#define SPELL_IMMOLATE 20294 + +struct MANGOS_DLL_DECL boss_sulfuronAI : public ScriptedAI +{ + boss_sulfuronAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Reset(); + } + + uint32 Darkstrike_Timer; + uint32 DemoralizingShout_Timer; + uint32 Inspire_Timer; + uint32 Knockdown_Timer; + uint32 Flamespear_Timer; + ScriptedInstance *pInstance; + + void Reset() + { + Darkstrike_Timer=10000; //These times are probably wrong + DemoralizingShout_Timer = 15000; + Inspire_Timer = 13000; + Knockdown_Timer = 6000; + Flamespear_Timer = 2000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //DemoralizingShout_Timer + if (DemoralizingShout_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_DEMORALIZINGSHOUT); + DemoralizingShout_Timer = 15000 + rand()%5000; + }else DemoralizingShout_Timer -= diff; + + //Inspire_Timer + if (Inspire_Timer < diff) + { + Creature* target = NULL; + std::list pList = DoFindFriendlyMissingBuff(45.0f,SPELL_INSPIRE); + if (!pList.empty()) + { + std::list::iterator i = pList.begin(); + advance(i, (rand()%pList.size())); + target = (*i); + } + + if (target) + DoCast(target,SPELL_INSPIRE); + + DoCast(m_creature,SPELL_INSPIRE); + + Inspire_Timer = 20000 + rand()%6000; + }else Inspire_Timer -= diff; + + //Knockdown_Timer + if (Knockdown_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCKDOWN); + Knockdown_Timer = 12000 + rand()%3000; + }else Knockdown_Timer -= diff; + + //Flamespear_Timer + if (Flamespear_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_FLAMESPEAR); + + Flamespear_Timer = 12000 + rand()%4000; + }else Flamespear_Timer -= diff; + + //DarkStrike_Timer + if (Darkstrike_Timer < diff) + { + DoCast(m_creature, SPELL_DARKSTRIKE); + Darkstrike_Timer = 15000 + rand()%3000; + }else Darkstrike_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_flamewaker_priestAI : public ScriptedAI +{ + mob_flamewaker_priestAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Reset(); + } + + uint32 Heal_Timer; + uint32 ShadowWordPain_Timer; + uint32 Immolate_Timer; + + ScriptedInstance *pInstance; + + void Reset() + { + Heal_Timer = 15000+rand()%15000; + ShadowWordPain_Timer = 2000; + Immolate_Timer = 8000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Casting Heal to Sulfuron or other Guards. + if(Heal_Timer < diff) + { + Unit* pUnit = DoSelectLowestHpFriendly(60.0f, 1); + if (!pUnit) + return; + + DoCast(pUnit, SPELL_HEAL); + + Heal_Timer = 15000+rand()%5000; + }else Heal_Timer -= diff; + + //ShadowWordPain_Timer + if (ShadowWordPain_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_SHADOWWORDPAIN); + + ShadowWordPain_Timer = 18000+rand()%8000; + }else ShadowWordPain_Timer -= diff; + + //Immolate_Timer + if (Immolate_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_IMMOLATE); + + Immolate_Timer = 15000+rand()%10000; + }else Immolate_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_sulfuron(Creature *_Creature) +{ + return new boss_sulfuronAI (_Creature); +} + +CreatureAI* GetAI_mob_flamewaker_priest(Creature *_Creature) +{ + return new mob_flamewaker_priestAI (_Creature); +} + +void AddSC_boss_sulfuron() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_sulfuron"; + newscript->GetAI = GetAI_boss_sulfuron; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_flamewaker_priest"; + newscript->GetAI = GetAI_mob_flamewaker_priest; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/def_molten_core.h b/src/bindings/scripts/scripts/zone/molten_core/def_molten_core.h new file mode 100644 index 00000000000..584adce0d31 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/def_molten_core.h @@ -0,0 +1,21 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_MOLTEN_CORE_H +#define DEF_MOLTEN_CORE_H + +#define DATA_FLAMEWAKERPRIEST 1 +#define DATA_GARRISDEAD 2 +#define DATA_GEDDONISDEAD 3 +#define DATA_GEHENNASISDEAD 4 +#define DATA_GOLEMAGGISDEAD 5 +#define DATA_GOLEMAGG_DEATH 6 +#define DATA_LUCIFRONISDEAD 7 +#define DATA_MAGMADARISDEAD 8 +#define DATA_MAJORDOMOISDEAD 9 +#define DATA_SHAZZRAHISDEAD 10 +#define DATA_SULFURON 11 +#define DATA_SULFURONISDEAD 12 +#define DATA_GOLEMAGG 13 +#endif diff --git a/src/bindings/scripts/scripts/zone/molten_core/instance_molten_core.cpp b/src/bindings/scripts/scripts/zone/molten_core/instance_molten_core.cpp new file mode 100644 index 00000000000..60521aa5f9c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/instance_molten_core.cpp @@ -0,0 +1,260 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Molten_Core +SD%Complete: 0 +SDComment: Place Holder +SDCategory: Molten Core +EndScriptData */ + +#include "precompiled.h" +#include "def_molten_core.h" + +#define ID_LUCIFRON 12118 +#define ID_MAGMADAR 11982 +#define ID_GEHENNAS 12259 +#define ID_GARR 12057 +#define ID_GEDDON 12056 +#define ID_SHAZZRAH 12264 +#define ID_GOLEMAGG 11988 +#define ID_SULFURON 12098 +#define ID_DOMO 12018 +#define ID_RAGNAROS 11502 +#define ID_FLAMEWAKERPRIEST 11662 + +class MANGOS_DLL_SPEC instance_molten_core : public ScriptedInstance +{ + public: + + instance_molten_core(Map *map) : ScriptedInstance(map) {} + + uint64 Lucifron, Magmadar, Gehennas, Garr, Geddon, Shazzrah, Sulfuron, Golemagg, Domo, Ragnaros, FlamewakerPriest; + uint64 RuneGUID[8]; + + //If all Bosses are dead. + bool IsBossDied[9]; + + uint32 CheckTimer; + + //On creation, NOT load. + void Initialize() + { + //Clear all GUIDs + Lucifron = 0; + Magmadar = 0; + Gehennas = 0; + Garr = 0; + Geddon = 0; + Shazzrah = 0; + Sulfuron = 0; + Golemagg = 0; + Domo = 0; + Ragnaros = 0; + FlamewakerPriest = 0; + + RuneGUID[0] = 0; + RuneGUID[1] = 0; + RuneGUID[2] = 0; + RuneGUID[3] = 0; + RuneGUID[4] = 0; + RuneGUID[5] = 0; + RuneGUID[6] = 0; + RuneGUID[7] = 0; + + IsBossDied[0] = false; + IsBossDied[1] = false; + IsBossDied[2] = false; + IsBossDied[3] = false; + IsBossDied[4] = false; + IsBossDied[5] = false; + IsBossDied[6] = false; + + IsBossDied[7] = false; + IsBossDied[8] = false; + + CheckTimer = 10000; + } + //Called every map update + void Update(uint32 diff) + { + + if (CheckTimer < diff) + { + //Check if all bosses are dead and activate Major Domo + + }else CheckTimer -= diff; + + } + + //Used by the map's CanEnter function. + //This is to prevent players from entering during boss encounters. + bool IsEncounterInProgress() const + { + return false; + }; + + //Called when a gameobject is created + void OnObjectCreate(GameObject *obj) + { + //Still searching for the individual rune ids. + //Currently they don't exist within most databases and are hard to find on websites + } + + //called on creature creation + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + //Store specific creatures based on entry id + switch (creature_entry) + { + case ID_LUCIFRON: + Lucifron = creature->GetGUID(); + break; + + case ID_MAGMADAR: + Magmadar = creature->GetGUID(); + break; + + case ID_GEHENNAS: + Gehennas = creature->GetGUID(); + break; + + case ID_GARR: + Garr = creature->GetGUID(); + break; + + case ID_GEDDON: + Geddon = creature->GetGUID(); + break; + + case ID_SHAZZRAH: + Shazzrah = creature->GetGUID(); + break; + + case ID_SULFURON: + Sulfuron = creature->GetGUID(); + break; + + case ID_GOLEMAGG: + Golemagg = creature->GetGUID(); + break; + + case ID_DOMO: + Domo = creature->GetGUID(); + break; + + case ID_RAGNAROS: + Ragnaros = creature->GetGUID(); + break; + + case ID_FLAMEWAKERPRIEST: + FlamewakerPriest = creature->GetGUID(); + break; + + default: + return; + } + } + + uint64 GetData64 (uint32 identifier) + { + switch(identifier) + { + case DATA_SULFURON: + return Sulfuron; + case DATA_GOLEMAGG: + return Sulfuron; + + case DATA_FLAMEWAKERPRIEST: + return FlamewakerPriest; + } + + return 0; + } // end GetData64 + + uint32 GetData(uint32 type) + { + switch(type) + { + case DATA_LUCIFRONISDEAD: + if(IsBossDied[0]) + return 1; + break; + + case DATA_MAGMADARISDEAD: + if(IsBossDied[1]) + return 1; + break; + + case DATA_GEHENNASISDEAD: + if(IsBossDied[2]) + return 1; + break; + + case DATA_GARRISDEAD: + if(IsBossDied[3]) + return 1; + break; + + case DATA_GEDDONISDEAD: + if(IsBossDied[4]) + return 1; + break; + + case DATA_SHAZZRAHISDEAD: + if(IsBossDied[5]) + return 1; + break; + + case DATA_SULFURONISDEAD: + if(IsBossDied[6]) + return 1; + break; + + case DATA_GOLEMAGGISDEAD: + if(IsBossDied[7]) + return 1; + break; + + case DATA_MAJORDOMOISDEAD: + if(IsBossDied[8]) + return 1; + break; + } + + return 0; + } + + void SetData(uint32 type, uint32 data) + { + if(type == DATA_GOLEMAGG_DEATH) + IsBossDied[7] = true; + } +}; + +InstanceData* GetInstance_instance_molten_core(Map *_Map) +{ + return new instance_molten_core (_Map); +} + +void AddSC_instance_molten_core() +{ + Script *newscript; + newscript = new Script; + newscript->Name="instance_molten_core"; + newscript->GetInstanceData = &GetInstance_instance_molten_core; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/molten_core/molten_core.cpp b/src/bindings/scripts/scripts/zone/molten_core/molten_core.cpp new file mode 100644 index 00000000000..c2aab542828 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/molten_core/molten_core.cpp @@ -0,0 +1,88 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Molten_Core +SD%Complete: 100 +SDComment: +SDCategory: Molten Core +EndScriptData */ + +/* ContentData +mob_ancient_core_hound +EndContentData */ + +#include "precompiled.h" +#include "../../creature/simple_ai.h" + +#define SPELL_CONE_OF_FIRE 19630 +#define SPELL_BITE 19771 + +//Random Debuff (each hound has only one of these) +#define SPELL_GROUND_STOMP 19364 +#define SPELL_ANCIENT_DREAD 19365 +#define SPELL_CAUTERIZING_FLAMES 19366 +#define SPELL_WITHERING_HEAT 19367 +#define SPELL_ANCIENT_DESPAIR 19369 +#define SPELL_ANCIENT_HYSTERIA 19372 + +CreatureAI* GetAI_mob_ancient_core_hound(Creature *_Creature) +{ + SimpleAI *ai = new SimpleAI(_Creature); + + ai->Spell[0].Enabled = true; + ai->Spell[0].Spell_Id = SPELL_CONE_OF_FIRE; + ai->Spell[0].Cooldown = 7000; + ai->Spell[0].First_Cast = 10000; + ai->Spell[0].Cast_Target_Type = CAST_HOSTILE_TARGET; + + uint32 RandDebuff; + switch(rand()%6) + { + case 0 : RandDebuff = SPELL_GROUND_STOMP; break; + case 1 : RandDebuff = SPELL_ANCIENT_DREAD; break; + case 2 : RandDebuff = SPELL_CAUTERIZING_FLAMES; break; + case 3 : RandDebuff = SPELL_WITHERING_HEAT; break; + case 4 : RandDebuff = SPELL_ANCIENT_DESPAIR; break; + case 5 : RandDebuff = SPELL_ANCIENT_HYSTERIA; break; + } + + ai->Spell[1].Enabled = true; + ai->Spell[1].Spell_Id = RandDebuff; + ai->Spell[1].Cooldown = 24000; + ai->Spell[1].First_Cast = 15000; + ai->Spell[1].Cast_Target_Type = CAST_HOSTILE_TARGET; + + ai->Spell[2].Enabled = true; + ai->Spell[2].Spell_Id = SPELL_BITE; + ai->Spell[2].Cooldown = 6000; + ai->Spell[2].First_Cast = 4000; + ai->Spell[2].Cast_Target_Type = CAST_HOSTILE_TARGET; + + ai->EnterEvadeMode(); + + return ai; +} + +void AddSC_molten_core() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mob_ancient_core_hound"; + newscript->GetAI = GetAI_mob_ancient_core_hound; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/moonglade/moonglade.cpp b/src/bindings/scripts/scripts/zone/moonglade/moonglade.cpp new file mode 100644 index 00000000000..d88aadf04ab --- /dev/null +++ b/src/bindings/scripts/scripts/zone/moonglade/moonglade.cpp @@ -0,0 +1,217 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Moonglade +SD%Complete: 100 +SDComment: Quest support: 30, 272, 5929, 5930. Special Flight Paths for Druid class. +SDCategory: Moonglade +EndScriptData */ + +/* ContentData +npc_bunthen_plainswind +npc_great_bear_spirit +npc_silva_filnaveth +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_bunthen_plainswind +######*/ + +bool GossipHello_npc_bunthen_plainswind(Player *player, Creature *_Creature) +{ + if(player->getClass() != CLASS_DRUID) + player->SEND_GOSSIP_MENU(4916,_Creature->GetGUID()); + else if(player->GetTeam() != HORDE) + { + if(player->GetQuestStatus(272) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Do you know where I can find Half Pendant of Aquatic Endurance?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + + player->SEND_GOSSIP_MENU(4917,_Creature->GetGUID()); + } + else if(player->getClass() == CLASS_DRUID && player->GetTeam() == HORDE) + { + player->ADD_GOSSIP_ITEM( 0, "I'd like to fly to Thunder Bluff.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + if(player->GetQuestStatus(30) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Do you know where I can find Half Pendant of Aquatic Endurance?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + + player->SEND_GOSSIP_MENU(4918,_Creature->GetGUID()); + } + return true; +} + +bool GossipSelect_npc_bunthen_plainswind(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch(action) + { + case GOSSIP_ACTION_INFO_DEF + 1: + { + player->CLOSE_GOSSIP_MENU(); + if (player->getClass() == CLASS_DRUID && player->GetTeam() == HORDE) + { + std::vector nodes; + + nodes.resize(2); + nodes[0] = 63; // Nighthaven, Moonglade + nodes[1] = 22; // Thunder Bluff, Mulgore + player->ActivateTaxiPathTo(nodes); + } + break; + } + case GOSSIP_ACTION_INFO_DEF + 2: + player->SEND_GOSSIP_MENU(5373,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + player->SEND_GOSSIP_MENU(5376,_Creature->GetGUID()); + break; + } + return true; +} + +/*###### +## npc_great_bear_spirit +######*/ + +#define GOSSIP_BEAR1 "What do you represent, spirit?" +#define GOSSIP_BEAR2 "I seek to understand the importance of strength of the body." +#define GOSSIP_BEAR3 "I seek to understand the importance of strength of the heart." +#define GOSSIP_BEAR4 "I have heard your words, Great Bear Spirit, and I understand. I now seek your blessings to fully learn the way of the Claw." + +bool GossipHello_npc_great_bear_spirit(Player *player, Creature *_Creature) +{ + //ally or horde quest + if (player->GetQuestStatus(5929) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(5930) == QUEST_STATUS_INCOMPLETE) + { + player->ADD_GOSSIP_ITEM( 0, GOSSIP_BEAR1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(4719, _Creature->GetGUID()); + } + else + player->SEND_GOSSIP_MENU(4718, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_great_bear_spirit(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_BEAR2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(4721, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 1: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_BEAR3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(4733, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_BEAR4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(4734, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + player->SEND_GOSSIP_MENU(4735, _Creature->GetGUID()); + if (player->GetQuestStatus(5929)==QUEST_STATUS_INCOMPLETE) + player->AreaExploredOrEventHappens(5929); + if (player->GetQuestStatus(5930)==QUEST_STATUS_INCOMPLETE) + player->AreaExploredOrEventHappens(5930); + break; + } + return true; +} + +/*###### +## npc_silva_filnaveth +######*/ + +bool GossipHello_npc_silva_filnaveth(Player *player, Creature *_Creature) +{ + if(player->getClass() != CLASS_DRUID) + player->SEND_GOSSIP_MENU(4913,_Creature->GetGUID()); + else if(player->GetTeam() != ALLIANCE) + { + if(player->GetQuestStatus(30) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Do you know where I can find Half Pendant of Aquatic Agility?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + + player->SEND_GOSSIP_MENU(4915,_Creature->GetGUID()); + } + else if(player->getClass() == CLASS_DRUID && player->GetTeam() == ALLIANCE) + { + player->ADD_GOSSIP_ITEM( 0, "I'd like to fly to Rut'theran Village.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + if(player->GetQuestStatus(272) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Do you know where I can find Half Pendant of Aquatic Agility?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + + player->SEND_GOSSIP_MENU(4914,_Creature->GetGUID()); + } + return true; +} + +bool GossipSelect_npc_silva_filnaveth(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch(action) + { + case GOSSIP_ACTION_INFO_DEF + 1: + { + player->CLOSE_GOSSIP_MENU(); + if (player->getClass() == CLASS_DRUID && player->GetTeam() == ALLIANCE) + { + std::vector nodes; + + nodes.resize(2); + nodes[0] = 62; // Nighthaven, Moonglade + nodes[1] = 27; // Rut'theran Village, Teldrassil + player->ActivateTaxiPathTo(nodes); + } + break; + } + case GOSSIP_ACTION_INFO_DEF + 2: + player->SEND_GOSSIP_MENU(5374,_Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + player->SEND_GOSSIP_MENU(5375,_Creature->GetGUID()); + break; + } + return true; +} + +/*###### +## +######*/ + +void AddSC_moonglade() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_bunthen_plainswind"; + newscript->pGossipHello = &GossipHello_npc_bunthen_plainswind; + newscript->pGossipSelect = &GossipSelect_npc_bunthen_plainswind; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_great_bear_spirit"; + newscript->pGossipHello = &GossipHello_npc_great_bear_spirit; + newscript->pGossipSelect = &GossipSelect_npc_great_bear_spirit; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_silva_filnaveth"; + newscript->pGossipHello = &GossipHello_npc_silva_filnaveth; + newscript->pGossipSelect = &GossipSelect_npc_silva_filnaveth; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/mulgore/mulgore.cpp b/src/bindings/scripts/scripts/zone/mulgore/mulgore.cpp new file mode 100644 index 00000000000..5e10e3500cb --- /dev/null +++ b/src/bindings/scripts/scripts/zone/mulgore/mulgore.cpp @@ -0,0 +1,64 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Mulgore +SD%Complete: 100 +SDComment: Skorn Whitecloud: Just a story if not rewarded for quest +SDCategory: Mulgore +EndScriptData */ + +/* ContentData +npc_skorn_whitecloud +EndContentData */ + +#include "precompiled.h" + +/*###### +# npc_skorn_whitecloud +######*/ + +bool GossipHello_npc_skorn_whitecloud(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (!player->GetQuestRewardStatus(770)) + player->ADD_GOSSIP_ITEM( 0, "Tell me a story, Skorn.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF ); + + player->SEND_GOSSIP_MENU(522,_Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_skorn_whitecloud(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF) + player->SEND_GOSSIP_MENU(523,_Creature->GetGUID()); + + return true; +} + +void AddSC_mulgore() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_skorn_whitecloud"; + newscript->pGossipHello = &GossipHello_npc_skorn_whitecloud; + newscript->pGossipSelect = &GossipSelect_npc_skorn_whitecloud; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/nagrand/nagrand.cpp b/src/bindings/scripts/scripts/zone/nagrand/nagrand.cpp new file mode 100644 index 00000000000..2713d2b2d94 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/nagrand/nagrand.cpp @@ -0,0 +1,568 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Nagrand +SD%Complete: 90 +SDComment: Quest support: 9849, 9918, 9874, 9991, 10107, 10108, 10044, 10172, 10646, 10085. TextId's unknown for altruis_the_sufferer and greatmother_geyah (npc_text) +SDCategory: Nagrand +EndScriptData */ + +/* ContentData +mob_shattered_rumbler +mob_lump +mob_sunspring_villager +npc_altruis_the_sufferer +npc_greatmother_geyah +npc_lantresor_of_the_blade +npc_creditmarker_visit_with_ancestors +EndContentData */ + +#include "precompiled.h" + +/*###### +## mob_shattered_rumbler - this should be done with ACID +######*/ + +struct MANGOS_DLL_DECL mob_shattered_rumblerAI : public ScriptedAI +{ + bool Spawn; + + mob_shattered_rumblerAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + Spawn = false; + } + + void Aggro(Unit* who) {} + + void SpellHit(Unit *Hitter, const SpellEntry *Spellkind) + { + if(Spellkind->Id == 32001 && !Spawn) + { + float x = m_creature->GetPositionX(); + float y = m_creature->GetPositionY(); + float z = m_creature->GetPositionZ(); + + Hitter->SummonCreature(18181,x+(0.7 * (rand()%30)),y+(rand()%5),z,0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,60000); + Hitter->SummonCreature(18181,x+(rand()%5),y-(rand()%5),z,0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,60000); + Hitter->SummonCreature(18181,x-(rand()%5),y+(0.5 *(rand()%60)),z,0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,60000); + m_creature->setDeathState(CORPSE); + Spawn = true; + } + return; + } +}; +CreatureAI* GetAI_mob_shattered_rumbler(Creature *_Creature) +{ + return new mob_shattered_rumblerAI (_Creature); +} + +/*###### +## mob_lump +######*/ + +#define SPELL_VISUAL_SLEEP 16093 +#define SPELL_SPEAR_THROW 32248 + +#define LUMP_SAY0 "In Nagrand, food hunt ogre!" +#define LUMP_SAY1 "You taste good with maybe a little salt and pepper." + +#define LUMP_DEFEAT "OK, OK! Lump give up!" + +struct MANGOS_DLL_DECL mob_lumpAI : public ScriptedAI +{ + mob_lumpAI(Creature *c) : ScriptedAI(c) + { + bReset = false; + Reset(); + } + + uint32 Reset_Timer; + uint32 Spear_Throw_Timer; + bool bReset; + + void Reset() + { + Reset_Timer = 60000; + Spear_Throw_Timer = 2000; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + + void DamageTaken(Unit *done_by, uint32 & damage) + { + if (done_by->GetTypeId() == TYPEID_PLAYER && (m_creature->GetHealth() - damage)*100 / m_creature->GetMaxHealth() < 30) + { + if (!bReset && ((Player*)done_by)->GetQuestStatus(9918) == QUEST_STATUS_INCOMPLETE) + { + //Take 0 damage + damage = 0; + + ((Player*)done_by)->AttackStop(); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(); + m_creature->setFaction(1080); //friendly + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, PLAYER_STATE_SIT); + m_creature->Say(LUMP_DEFEAT, LANG_UNIVERSAL, 0); + + bReset = true; + } + } + } + + void Aggro(Unit *who) + { + if (m_creature->HasAura(SPELL_VISUAL_SLEEP,0)) + m_creature->RemoveAura(SPELL_VISUAL_SLEEP,0); + + if (!m_creature->IsStandState()) + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, PLAYER_STATE_NONE); + + switch(rand()%2) + { + case 0: + DoSay(LUMP_SAY0,LANG_UNIVERSAL,NULL); + break; + case 1: + DoSay(LUMP_SAY1,LANG_UNIVERSAL,NULL); + break; + } + } + + void UpdateAI(const uint32 diff) + { + //check if we waiting for a reset + if (bReset) + { + if (Reset_Timer < diff) + { + EnterEvadeMode(); + bReset = false; + m_creature->setFaction(1711); //hostile + } + else Reset_Timer -= diff; + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Spear_Throw_Timer + if (Spear_Throw_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SPEAR_THROW); + Spear_Throw_Timer = 20000; + }else Spear_Throw_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_lump(Creature *_creature) +{ + return new mob_lumpAI(_creature); +} + +bool GossipHello_mob_lump(Player *player, Creature *_Creature) +{ + if (player->GetQuestStatus(9918) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "I need answers, ogre!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(9352, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_mob_lump(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, "Why are Boulderfist out this far? You know that this is Kurenai territory.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(9353, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "And you think you can just eat anything you want? You're obviously trying to eat the Broken of Telaar.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(9354, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "This means war, Lump! War I say!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(9355, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->SEND_GOSSIP_MENU(9356, _Creature->GetGUID()); + player->TalkedToCreature(18354, _Creature->GetGUID()); + break; + } + return true; +} + +/*#### +# mob_sunspring_villager - should be done with ACID +####*/ + +struct MANGOS_DLL_DECL mob_sunspring_villagerAI : public ScriptedAI +{ + mob_sunspring_villagerAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + m_creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 32); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,7); // lay down + } + + void Aggro(Unit *who) {} + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + if(spell->Id == 32146) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + m_creature->RemoveCorpse(); + } + } +}; +CreatureAI* GetAI_mob_sunspring_villager(Creature *_Creature) +{ + return new mob_sunspring_villagerAI (_Creature); +} + +/*###### +## npc_altruis_the_sufferer +######*/ + +bool GossipHello_npc_altruis_the_sufferer(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + //gossip before obtaining Survey the Land + if ( player->GetQuestStatus(9991) == QUEST_STATUS_NONE ) + player->ADD_GOSSIP_ITEM( 0, "I see twisted steel and smell sundered earth.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+10); + + //gossip when Survey the Land is incomplete (technically, after the flight) + if (player->GetQuestStatus(9991) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Well...?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+20); + + //wowwiki.com/Varedis + if (player->GetQuestStatus(10646) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "[PH] Story about Illidan's Pupil", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+30); + + player->SEND_GOSSIP_MENU(9419, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_altruis_the_sufferer(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+10: + player->ADD_GOSSIP_ITEM( 0, "Legion?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(9420, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+11: + player->ADD_GOSSIP_ITEM( 0, "And now?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(9421, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+12: + player->ADD_GOSSIP_ITEM( 0, "How do you see them now?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13); + player->SEND_GOSSIP_MENU(9422, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+13: + player->ADD_GOSSIP_ITEM( 0, "Forge camps?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14); + player->SEND_GOSSIP_MENU(9423, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+14: + player->SEND_GOSSIP_MENU(9424, _Creature->GetGUID()); + break; + + case GOSSIP_ACTION_INFO_DEF+20: + player->ADD_GOSSIP_ITEM( 0, "Ok.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 21); + player->SEND_GOSSIP_MENU(9427, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+21: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(9991); + break; + + case GOSSIP_ACTION_INFO_DEF+30: + player->ADD_GOSSIP_ITEM( 0, "[PH] Story done", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 31); + player->SEND_GOSSIP_MENU(384, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+31: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(10646); + break; + } + return true; +} + +bool QuestAccept_npc_altruis_the_sufferer(Player *player, Creature *creature, Quest const *quest ) +{ + if ( !player->GetQuestRewardStatus(9991) ) //Survey the Land, q-id 9991 + { + player->CLOSE_GOSSIP_MENU(); + + std::vector nodes; + + nodes.resize(2); + nodes[0] = 113; //from + nodes[1] = 114; //end at + player->ActivateTaxiPathTo(nodes); //TaxiPath 532 + } + return true; +} + +/*###### +## npc_greatmother_geyah +######*/ + +//all the textId's for the below is unknown, but i do believe the gossip item texts are proper. +bool GossipHello_npc_greatmother_geyah(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(10044) == QUEST_STATUS_INCOMPLETE) + { + player->ADD_GOSSIP_ITEM( 0, "Hello, Greatmother. Garrosh told me that you wanted to speak with me.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(),_Creature->GetGUID()); + } + else if (player->GetQuestStatus(10172) == QUEST_STATUS_INCOMPLETE) + { + player->ADD_GOSSIP_ITEM( 0, "Garrosh is beyond redemption, Greatmother. I fear that in helping the Mag'har, I have convinced Garrosh that he is unfit to lead.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(),_Creature->GetGUID()); + } + else + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(),_Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_greatmother_geyah(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: + player->ADD_GOSSIP_ITEM( 0, "You raised all of the orcs here, Greatmother?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + player->ADD_GOSSIP_ITEM( 0, "Do you believe that?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + player->ADD_GOSSIP_ITEM( 0, "What can be done? I have tried many different things. I have done my best to help the people of Nagrand. Each time I have approached Garrosh, he has dismissed me.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: + player->ADD_GOSSIP_ITEM( 0, "Left? How can you choose to leave?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: + player->ADD_GOSSIP_ITEM( 0, "What is this duty?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: + player->ADD_GOSSIP_ITEM( 0, "Is there anything I can do for you, Greatmother?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 7: + player->AreaExploredOrEventHappens(10044); + player->CLOSE_GOSSIP_MENU(); + break; + + case GOSSIP_ACTION_INFO_DEF + 10: + player->ADD_GOSSIP_ITEM( 0, "I have done all that I could, Greatmother. I thank you for your kind words.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: + player->ADD_GOSSIP_ITEM( 0, "Greatmother, you are the mother of Durotan?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: + player->ADD_GOSSIP_ITEM( 0, "Greatmother, I never had the honor. Durotan died long before my time, but his heroics are known to all on my world. The orcs of Azeroth reside in a place known as Durotar, named after your son. And ... (You take a moment to breathe and think through what you are about to tell the Greatmother.)", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 13: + player->ADD_GOSSIP_ITEM( 0, "It is my Warchief, Greatmother. The leader of my people. From my world. He ... He is the son of Durotan. He is your grandchild.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 14: + player->ADD_GOSSIP_ITEM( 0, "I will return to Azeroth at once, Greatmother.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 15); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 15: + player->AreaExploredOrEventHappens(10172); + player->CLOSE_GOSSIP_MENU(); + break; + } + return true; +} + +/*###### +## npc_lantresor_of_the_blade +######*/ + +bool GossipHello_npc_lantresor_of_the_blade(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(10107) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(10108) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "I have killed many of your ogres, Lantresor. I have no fear.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(9361, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_lantresor_of_the_blade(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, "Should I know? You look like an orc to me.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(9362, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "And the other half?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(9363, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "I have heard of your kind, but I never thought to see the day when I would meet a half-breed.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(9364, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, "My apologies. I did not mean to offend. I am here on behalf of my people.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->SEND_GOSSIP_MENU(9365, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM( 0, "My people ask that you pull back your Boulderfist ogres and cease all attacks on our territories. In return, we will also pull back our forces.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(9366, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->ADD_GOSSIP_ITEM( 0, "We will fight you until the end, then, Lantresor. We will not stand idly by as you pillage our towns and kill our people.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(9367, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + player->ADD_GOSSIP_ITEM( 0, "What do I need to do?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->SEND_GOSSIP_MENU(9368, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+7: + player->SEND_GOSSIP_MENU(9369, _Creature->GetGUID()); + if (player->GetQuestStatus(10107) == QUEST_STATUS_INCOMPLETE) + player->AreaExploredOrEventHappens(10107); + if (player->GetQuestStatus(10108) == QUEST_STATUS_INCOMPLETE) + player->AreaExploredOrEventHappens(10108); + break; + } + return true; +} + +/*###### +## npc_creditmarker_visist_with_ancestors (Quest 10085) +######*/ + +struct MANGOS_DLL_DECL npc_creditmarker_visit_with_ancestorsAI : public ScriptedAI +{ + npc_creditmarker_visit_with_ancestorsAI(Creature* c) : ScriptedAI(c) { Reset(); } + + void Reset() {} + + void Aggro(Unit* who) {} + + void MoveInLineOfSight(Unit *who) + { + if(!who) + return; + + if(who->GetTypeId() == TYPEID_PLAYER) + { + if(((Player*)who)->GetQuestStatus(10085) == QUEST_STATUS_INCOMPLETE) + { + uint32 creditMarkerId = m_creature->GetEntry(); + if((creditMarkerId >= 18840) && (creditMarkerId <= 18843)) + { + // 18840: Sunspring, 18841: Laughing, 18842: Garadar, 18843: Bleeding + if(!((Player*)who)->GetReqKillOrCastCurrentCount(10085, creditMarkerId)) + ((Player*)who)->KilledMonster(creditMarkerId, m_creature->GetGUID()); + } + } + } + } +}; + +CreatureAI* GetAI_npc_creditmarker_visit_with_ancestors(Creature *_Creature) +{ + return new npc_creditmarker_visit_with_ancestorsAI (_Creature); +} + +/*###### +## AddSC +######*/ + +void AddSC_nagrand() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mob_shattered_rumbler"; + newscript->GetAI = GetAI_mob_shattered_rumbler; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_lump"; + newscript->GetAI = GetAI_mob_lump; + newscript->pGossipHello = &GossipHello_mob_lump; + newscript->pGossipSelect = &GossipSelect_mob_lump; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_sunspring_villager"; + newscript->GetAI = GetAI_mob_sunspring_villager; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_altruis_the_sufferer"; + newscript->pGossipHello = &GossipHello_npc_altruis_the_sufferer; + newscript->pGossipSelect = &GossipSelect_npc_altruis_the_sufferer; + newscript->pQuestAccept = &QuestAccept_npc_altruis_the_sufferer; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_greatmother_geyah"; + newscript->pGossipHello = &GossipHello_npc_greatmother_geyah; + newscript->pGossipSelect = &GossipSelect_npc_greatmother_geyah; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_lantresor_of_the_blade"; + newscript->pGossipHello = &GossipHello_npc_lantresor_of_the_blade; + newscript->pGossipSelect = &GossipSelect_npc_lantresor_of_the_blade; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_creditmarker_visit_with_ancestors"; + newscript->GetAI = GetAI_npc_creditmarker_visit_with_ancestors; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_anubrekhan.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_anubrekhan.cpp new file mode 100644 index 00000000000..d2dbd710cc9 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_anubrekhan.cpp @@ -0,0 +1,215 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Anubrekhan +SD%Complete: 100 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +#define SAY_AGGRO1 "Just a little taste..." +#define SAY_AGGRO2 "There is no way out." +#define SAY_AGGRO3 "Yes, Run! It makes the blood pump faster!" +#define SAY_GREET "Ahh... welcome to my parlor" +#define SAY_TAUNT1 "I hear little hearts beating. Yesss... beating faster now. Soon the beating will stop." +#define SAY_TAUNT2 "Where to go? What to do? So many choices that all end in pain, end in death." +#define SAY_TAUNT3 "Which one shall I eat first? So difficult to choose... the all smell so delicious." +#define SAY_TAUNT4 "Closer now... tasty morsels. I've been too long without food. Without blood to drink." +#define SAY_SLAY "Shh... it will all be over soon." + +#define SOUND_AGGRO1 8785 +#define SOUND_AGGRO2 8786 +#define SOUND_AGGRO3 8787 +#define SOUND_GREET 8788 +#define SOUND_TAUNT1 8790 +#define SOUND_TAUNT2 8791 +#define SOUND_TAUNT3 8792 +#define SOUND_TAUNT4 8793 +#define SOUND_SLAY 8789 + +#define SPELL_IMPALE 28783 //May be wrong spell id. Causes more dmg than I expect +#define SPELL_LOCUSTSWARM 28785 //This is a self buff that triggers the dmg debuff +#define SPELL_SUMMONGUARD 29508 //Summons 1 crypt guard at targeted location + +#define SPELL_SELF_SPAWN_5 29105 //This spawns 5 corpse scarabs ontop of us (most likely the player casts this on death) +#define SPELL_SELF_SPAWN_10 28864 //This is used by the crypt guards when they die + +struct MANGOS_DLL_DECL boss_anubrekhanAI : public ScriptedAI +{ + boss_anubrekhanAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Impale_Timer; + uint32 LocustSwarm_Timer; + uint32 Summon_Timer; + bool HasTaunted; + + void Reset() + { + Impale_Timer = 15000; //15 seconds + LocustSwarm_Timer = 80000 + (rand()%40000); //Random time between 80 seconds and 2 minutes for initial cast + Summon_Timer = LocustSwarm_Timer + 45000; //45 seconds after initial locust swarm + } + + void KilledUnit(Unit* Victim) + { + //Force the player to spawn corpse scarabs via spell + Victim->CastSpell(Victim, SPELL_SELF_SPAWN_5, true); + + if (rand()%5) + return; + + DoYell(SAY_SLAY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY); + } + + void Aggro(Unit *who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO3); + break; + } + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + //Begin melee attack if we are within range + DoStartAttackAndMovement(who); + + if (!InCombat) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO3); + break; + } + } + } + else if (!HasTaunted && m_creature->IsWithinDistInMap(who, 60.0f)) + { + switch(rand()%5) + { + case 0: + DoYell(SAY_TAUNT1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_TAUNT1); + break; + case 1: + DoYell(SAY_TAUNT2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_TAUNT2); + break; + case 2: + DoYell(SAY_TAUNT3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_TAUNT3); + break; + case 3: + DoYell(SAY_TAUNT4, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_TAUNT4); + break; + case 4: + DoYell(SAY_GREET, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_GREET); + break; + } + HasTaunted = true; + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Impale_Timer + if (Impale_Timer < diff) + { + //Cast Impale on a random target + //Do NOT cast it when we are afflicted by locust swarm + if (!m_creature->HasAura(SPELL_LOCUSTSWARM,1)) + { + Unit* target = NULL; + + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target)DoCast(target,SPELL_IMPALE); + } + + Impale_Timer = 15000; + }else Impale_Timer -= diff; + + //LocustSwarm_Timer + if (LocustSwarm_Timer < diff) + { + DoCast(m_creature, SPELL_LOCUSTSWARM); + LocustSwarm_Timer = 90000; + }else LocustSwarm_Timer -= diff; + + //Summon_Timer + if (Summon_Timer < diff) + { + DoCast(m_creature, SPELL_SUMMONGUARD); + Summon_Timer = 45000; + }else Summon_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_anubrekhan(Creature *_Creature) +{ + return new boss_anubrekhanAI (_Creature); +} + +void AddSC_boss_anubrekhan() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_anubrekhan"; + newscript->GetAI = GetAI_boss_anubrekhan; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_faerlina.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_faerlina.cpp new file mode 100644 index 00000000000..3faf9ad9634 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_faerlina.cpp @@ -0,0 +1,202 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Faerlina +SD%Complete: 50 +SDComment: Without Mindcontrol boss cannot be defeated +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +#define SAY_GREET "Your old lives, your mortal desires, mean nothing. You are acolytes of the master now, and you will serve the cause without question! The greatest glory is to die in the master's service!" +#define SAY_AGGRO1 "Slay them in the master's name!" +#define SAY_AGGRO2 "You cannot hide from me!" +#define SAY_AGGRO3 "Kneel before me, worm!" +#define SAY_AGGRO4 "Run while you still can!" +#define SAY_SLAY1 "You have failed!" +#define SAY_SLAY2 "Pathetic wretch!" +#define SAY_DEATH "The master... will avenge me!" +#define SAY_RANDOM_AGGRO "???" + +#define SOUND_GREET 8799 +#define SOUND_AGGRO1 8794 +#define SOUND_AGGRO2 8795 +#define SOUND_AGGRO3 8796 +#define SOUND_AGGRO4 8797 +#define SOUND_SLAY1 8800 +#define SOUND_SLAY2 8801 +#define SOUND_DEATH 8798 +#define SOUND_RANDOM_AGGRO 8955 + +#define SPELL_POSIONBOLT_VOLLEY 28796 +#define SPELL_RAINOFFIRE 28794 //Not sure if targeted AoEs work if casted directly upon a player +#define SPELL_ENRAGE 26527 + +struct MANGOS_DLL_DECL boss_faerlinaAI : public ScriptedAI +{ + boss_faerlinaAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 PoisonBoltVolley_Timer; + uint32 RainOfFire_Timer; + uint32 Enrage_Timer; + bool HasTaunted; + + void Reset() + { + PoisonBoltVolley_Timer = 8000; + RainOfFire_Timer = 16000; + Enrage_Timer = 60000; + HasTaunted = false; + } + + void Aggro(Unit *who) + { + switch (rand()%4) + { + case 0: + DoYell(SAY_AGGRO1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO3); + break; + case 3: + DoYell(SAY_AGGRO4,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO4); + break; + } + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + //Say our dialog on initial aggro + if (!InCombat) + { + switch (rand()%4) + { + case 0: + DoYell(SAY_AGGRO1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO3); + break; + case 3: + DoYell(SAY_AGGRO4,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO4); + break; + } + } + } + else if (!HasTaunted && m_creature->IsWithinDistInMap(who, 60.0f)) + { + DoYell(SAY_GREET, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_GREET); + HasTaunted = true; + } + } + } + + void KilledUnit(Unit* victim) + { + switch (rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //PoisonBoltVolley_Timer + if (PoisonBoltVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_POSIONBOLT_VOLLEY); + PoisonBoltVolley_Timer = 11000; + }else PoisonBoltVolley_Timer -= diff; + + //RainOfFire_Timer + if (RainOfFire_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_RAINOFFIRE); + + RainOfFire_Timer = 16000; + }else RainOfFire_Timer -= diff; + + //Enrage_Timer + if (Enrage_Timer < diff) + { + DoCast(m_creature,SPELL_ENRAGE); + Enrage_Timer = 61000; + }else Enrage_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_faerlina(Creature *_Creature) +{ + return new boss_faerlinaAI (_Creature); +} + +void AddSC_boss_faerlina() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_faerlina"; + newscript->GetAI = GetAI_boss_faerlina; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_feugen.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_feugen.cpp new file mode 100644 index 00000000000..fd03b1757f5 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_feugen.cpp @@ -0,0 +1,33 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Feugen +SD%Complete: 0 +SDComment: Merge with Thaddius +SDCategory: Naxxramas +EndScriptData */ + +//Feugen +//8801 aggro Feed you to master! +//8804 slay Feugen make master happy! +//8803 death No... more... Feugen... + +#include "precompiled.h" + +#define SPELL_WARSTOMP 28125 +#define SPELL_MANABURN 28135 +#define SPELL_CHAIN_LIGHTNING 28900 diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_gluth.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_gluth.cpp new file mode 100644 index 00000000000..72b9b6628de --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_gluth.cpp @@ -0,0 +1,174 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Gluth +SD%Complete: 100 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_MORTALWOUND 25646 +#define SPELL_DECIMATE 28374 +#define SPELL_TERRIFYINGROAR 29685 +#define SPELL_FRENZY 19812 +#define SPELL_ENRAGE 28747 + +#define ADD_1X 3269.590 +#define ADD_1Y -3161.287 +#define ADD_1Z 297.423 + +#define ADD_2X 3277.797 +#define ADD_2Y -3170.352 +#define ADD_2Z 297.423 + +#define ADD_3X 3267.049 +#define ADD_3Y -3172.820 +#define ADD_3Z 297.423 + +#define ADD_4X 3252.157 +#define ADD_4Y -3132.135 +#define ADD_4Z 297.423 + +#define ADD_5X 3259.990 +#define ADD_5Y -3126.590 +#define ADD_5Z 297.423 + +#define ADD_6X 3259.815 +#define ADD_6Y -3137.576 +#define ADD_6Z 297.423 + +#define ADD_7X 3308.030 +#define ADD_7Y -3132.135 +#define ADD_7Z 297.423 + +#define ADD_8X 3303.046 +#define ADD_8Y -3180.682 +#define ADD_8Z 297.423 + +#define ADD_9X 3313.283 +#define ADD_9Y -3180.766 +#define ADD_9Z 297.423 + +struct MANGOS_DLL_DECL boss_gluthAI : public ScriptedAI +{ + boss_gluthAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 MortalWound_Timer; + uint32 Decimate_Timer; + uint32 TerrifyingRoar_Timer; + uint32 Frenzy_Timer; + uint32 Enrage_Timer; + uint32 Summon_Timer; + + void Reset() + { + MortalWound_Timer = 8000; + Decimate_Timer = 100000; + TerrifyingRoar_Timer = 21000; + Frenzy_Timer = 15000; + Enrage_Timer = 304000; + Summon_Timer = 10000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //MortalWound_Timer + if (MortalWound_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MORTALWOUND); + MortalWound_Timer = 10000; + }else MortalWound_Timer -= diff; + + //Decimate_Timer + if (Decimate_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_DECIMATE); + Decimate_Timer = 100000; + }else Decimate_Timer -= diff; + + //TerrifyingRoar_Timer + if (TerrifyingRoar_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_TERRIFYINGROAR); + TerrifyingRoar_Timer = 20000; + }else TerrifyingRoar_Timer -= diff; + + //Frenzy_Timer + if (Frenzy_Timer < diff) + { + DoCast(m_creature,SPELL_FRENZY); + Frenzy_Timer = 10500; + }else Frenzy_Timer -= diff; + + //Enrage_Timer + if (Enrage_Timer < diff) + { + DoCast(m_creature,SPELL_ENRAGE); + Enrage_Timer = 61000; + }else Enrage_Timer -= diff; + + //Summon_Timer + if (Summon_Timer < diff) + { + Unit* target = NULL; + Unit* SummonedZombies = NULL; + + SummonedZombies = m_creature->SummonCreature(16360,ADD_1X,ADD_1Y,ADD_1Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedZombies = m_creature->SummonCreature(16360,ADD_2X,ADD_2Y,ADD_2Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedZombies = m_creature->SummonCreature(16360,ADD_3X,ADD_3Y,ADD_3Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedZombies = m_creature->SummonCreature(16360,ADD_4X,ADD_4Y,ADD_4Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedZombies = m_creature->SummonCreature(16360,ADD_5X,ADD_5Y,ADD_5Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedZombies = m_creature->SummonCreature(16360,ADD_6X,ADD_6Y,ADD_6Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedZombies = m_creature->SummonCreature(16360,ADD_7X,ADD_7Y,ADD_7Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedZombies = m_creature->SummonCreature(16360,ADD_8X,ADD_8Y,ADD_8Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedZombies = m_creature->SummonCreature(16360,ADD_9X,ADD_9Y,ADD_9Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + + if (SummonedZombies) + { + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + SummonedZombies->AddThreat(target,1.0f); + } + + Summon_Timer = 28000; + } else Summon_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_gluth(Creature *_Creature) +{ + return new boss_gluthAI (_Creature); +} + +void AddSC_boss_gluth() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_gluth"; + newscript->GetAI = GetAI_boss_gluth; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_gothik.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_gothik.cpp new file mode 100644 index 00000000000..3be2727fe16 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_gothik.cpp @@ -0,0 +1,62 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Gothik +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Naxxramas +EndScriptData */ + +//Gothik +//8806 spch - Foolishly you have sought your own demise. Brazenly you have disregarded powers beyond your understanding. You have fought hard to invade the realm of the harvester. Now there is only one way out - to walk the lonely path of the damned. +//8808 tlpt - I have waited long enough! Now, you face the harvester of souls! +//8806 slay - Death is the only escape. +//8805 death - I... am... undone! + +#include "precompiled.h" + +//Gothik +#define SPELL_HARVESTSOUL 28679 +#define SPELL_SHADOWBOLT 19729 + +//Unrelenting Trainee +#define SPELL_EAGLECLAW 30285 +#define SPELL_KNOCKDOWN_PASSIVE 6961 + +//Unrelenting Deathknight +#define SPELL_CHARGE 22120 +#define SPELL_SHADOW_MARK 27825 + +//Unrelenting Rider +#define SPELL_UNHOLY_AURA 28340 +#define SPELL_SHADOWBOLT 19729 //Search thru targets and find those who have the SHADOW_MARK to cast this on + +//Spectral Trainee +#define SPELL_ARCANE_EXPLOSION 27989 + +//Spectral Deathknight +#define SPELL_WHIRLWIND 28334 +#define SPELL_SUNDER_ARMOR 25051 //cannot find sunder that reduces armor by 2950 +#define SPELL_CLEAVE 20677 +#define SPELL_MANA_BURN 17631 + +//Spectral Rider +#define SPELL_LIFEDRAIN 24300 +//USES SAME UNHOLY AURA AS UNRELENTING RIDER + +//Spectral Horse +#define SPELL_STOMP 27993 diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_grobbulus.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_grobbulus.cpp new file mode 100644 index 00000000000..cbe7657ed57 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_grobbulus.cpp @@ -0,0 +1,30 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Grobbulus +SD%Complete: 0 +SDComment: Place holder +SDCategory: Naxxramas +EndScriptData */ + +/*Poison Cloud 26590 +Slime Spray 28157 +Fallout slime 28218 +Mutating Injection 28169 +Enrages 26527*/ + +#include "precompiled.h" diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_heigan.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_heigan.cpp new file mode 100644 index 00000000000..147eae49e22 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_heigan.cpp @@ -0,0 +1,46 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Heigan +SD%Complete: 0 +SDComment: Place Holder +SDCategory: Naxxramas +EndScriptData */ + +//Lotheb or Heigan? +//8825 aggro1 - You are mine now! +//8826 aggro2 - I see you! +//8827 aggro3 - You...are next! +//8828 death - +//8829 slay - close your eyes... sleep +//8830 taunt1 - The races of the world will perish. It is only a matter of time. +//8831 taunt2 - I see endless suffering, I see torment, I see rage. I see... everything! +//8832 taunt3 - Soon... the world will tremble! +//8833 taunt4 - The end is upon you. +//8834 taunt5 - Hungry worms will feast on your rotten flesh! + +#include "precompiled.h" + +//Spell used by floor peices to cause damage to players +#define SPELL_ERUPTION 29371 + +//Spells by boss +#define SPELL_WILT 23772 +#define SPELL_FEAVER 29998 + +//Spell by eye stalks +#define SPELL_MIND_FLAY 26143 diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_highlord_mograine.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_highlord_mograine.cpp new file mode 100644 index 00000000000..0641d6d66a3 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_highlord_mograine.cpp @@ -0,0 +1,178 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Highlord_Mograine +SD%Complete: 100 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +//All horsemen +#define SPELL_SHIELDWALL 29061 +#define SPELL_BESERK 26662 + +// highlord mograine +#define SPELL_MARK_OF_MOGRAINE 28834 +#define SPELL_RIGHTEOUS_FIRE 28882 // Applied as a 25% chance on melee hit to proc. m_creature->GetVictim() + +#define SAY_TAUNT1 "Enough prattling. Let them come! We shall grind their bones to dust." +#define SAY_TAUNT2 "Conserve your anger! Harness your rage! You will all have outlets for your frustration soon enough." +#define SAY_TAUNT3 "Life is meaningless. It is in death that we are truly tested." +#define SAY_AGGRO1 "You seek death?" +#define SAY_AGGRO2 "None shall pass!" +#define SAY_AGGRO3 "Be still!" +#define SAY_SLAY1 "You will find no peace in death." +#define SAY_SLAY2 "The master's will is done." +#define SAY_SPECIAL "Bow to the might of the Highlord!" +#define SAY_DEATH "I... am... released! Perhaps it's not too late to - noo! I need... more time..." + +#define SOUND_TAUNT1 8842 +#define SOUND_TAUNT2 8843 +#define SOUND_TAUNT3 8844 +#define SOUND_AGGRO1 8835 +#define SOUND_AGGRO2 8836 +#define SOUND_AGGRO3 8837 +#define SOUND_SLAY1 8839 +#define SOUND_SLAY2 8840 +#define SOUND_SPECIAL 8841 +#define SOUND_DEATH 8838 + +#define SPIRIT_OF_MOGRAINE 16775 + +struct MANGOS_DLL_DECL boss_highlord_mograineAI : public ScriptedAI +{ + boss_highlord_mograineAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Mark_Timer; + uint32 RighteousFire_Timer; + bool ShieldWall1; + bool ShieldWall2; + + void Reset() + { + Mark_Timer = 20000; // First Horsemen Mark is applied at 20 sec. + RighteousFire_Timer = 2000; // applied approx 1 out of 4 attacks + ShieldWall1 = true; + ShieldWall2 = true; + } + + void InitialYell() + { + if(!InCombat) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_AGGRO1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO3); + break; + } + } + } + + void KilledUnit() + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void Aggro(Unit *who) + { + InitialYell(); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + // Mark of Mograine + if(Mark_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MARK_OF_MOGRAINE); + Mark_Timer = 12000; + }else Mark_Timer -= diff; + + // Shield Wall - All 4 horsemen will shield wall at 50% hp and 20% hp for 20 seconds + if(ShieldWall1 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 50) + { + if(ShieldWall1) + { + DoCast(m_creature,SPELL_SHIELDWALL); + ShieldWall1 = false; + } + } + if(ShieldWall2 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20) + { + if(ShieldWall2) + { + DoCast(m_creature,SPELL_SHIELDWALL); + ShieldWall2 = false; + } + } + + // Righteous Fire + if(RighteousFire_Timer < diff) + { + if(rand()%4 == 1) // 1/4 + { + DoCast(m_creature->getVictim(),SPELL_RIGHTEOUS_FIRE); + } + RighteousFire_Timer = 2000; + }else RighteousFire_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_highlord_mograine(Creature *_Creature) +{ + return new boss_highlord_mograineAI (_Creature); +} + +void AddSC_boss_highlord_mograine() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_highlord_mograine"; + newscript->GetAI = GetAI_boss_highlord_mograine; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_kelthuzad.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_kelthuzad.cpp new file mode 100644 index 00000000000..4f8e2e9470f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_kelthuzad.cpp @@ -0,0 +1,542 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_KelThuzud +SD%Complete: 0 +SDComment: VERIFY SCRIPT +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +//***THIS SCRIPTS IS UNDER DEVELOPMENT*** +/* +DATA. +This script has been made with info taken from wowwikki... so there are things wrong... +like spell timers and Says. Also there's another major problem as there is no aggroed list +I cannot make Kel'thuzad to target specific party members, that is needed for spells like +Mana Detonation... so what I'm doing untill now is just to cast everything on my main aggroed +target. Sorry for him. +Another bug is that there are spells that are actually NOT working... I have to implement +them first. +Need DISPELL efect +I also don't know the emotes +*/ + +//Kel'thuzad. Still don't know where to use these... +//Dialog +//8878 dialog01 - Our preparations continue as planned, master. +//8881 lich_naxx_dialog01 - It is good that you serve me so faithfully. Soon, all will serve the Lich King and in the end, you shall be rewarded...so long as you do not falter. +//8879 dialog02 - I see no complications... Wait... What is this? +//8882 lich_naxx_dialog03 - Your security measures have failed! See to this interruption immediately! +//8880 dialog03 - Yes, master! +//Bigglesworth death +//No!!! A curse upon you, interlopers! The armies of the Lich King will hunt you down. You will not escape your fate... +//slay +//8818 + +//common needed defines +#define NAXXRAMAS_MAP 533 +//Positional defines +#define ADDX_LEFT_FAR 3783.272705 +#define ADDY_LEFT_FAR -5062.697266 +#define ADDZ_LEFT_FAR 143.711203 +#define ADDO_LEFT_FAR 3.617599 + +#define ADDX_LEFT_MIDDLE 3730.291260 +#define ADDY_LEFT_MIDDLE -5027.239258 +#define ADDZ_LEFT_MIDDLE 143.956909 +#define ADDO_LEFT_MIDDLE 4.461900 + +#define ADDX_LEFT_NEAR 3683.868652 +#define ADDY_LEFT_NEAR -5057.281250 +#define ADDZ_LEFT_NEAR 143.183884 +#define ADDO_LEFT_NEAR 5.237086 + +#define ADDX_RIGHT_FAR 3759.355225 +#define ADDY_RIGHT_FAR -5174.128418 +#define ADDZ_RIGHT_FAR 143.802383 +#define ADDO_RIGHT_FAR 2.170104 + +#define ADDX_RIGHT_MIDDLE 370.724365 +#define ADDY_RIGHT_MIDDLE -5185.123047 +#define ADDZ_RIGHT_MIDDLE 143.928024 +#define ADDO_RIGHT_MIDDLE 1.309310 + +#define ADDX_RIGHT_NEAR 3665.121094 +#define ADDY_RIGHT_NEAR -5138.679199 +#define ADDZ_RIGHT_NEAR 143.183212 +#define ADDO_RIGHT_NEAR 0.604023 + +#define WALKX_LEFT_FAR 3754.431396 +#define WALKY_LEFT_FAR -5080.727734 +#define WALKZ_LEFT_FAR 142.036316 +#define WALKO_LEFT_FAR 3.736189 + +#define WALKX_LEFT_MIDDLE 3724.396484 +#define WALKY_LEFT_MIDDLE -5061.330566 +#define WALKZ_LEFT_MIDDLE 142.032700 +#define WALKO_LEFT_MIDDLE 4.564785 + +#define WALKX_LEFT_NEAR 3687.158424 +#define WALKY_LEFT_NEAR -5076.834473 +#define WALKZ_LEFT_NEAR 142.017319 +#define WALKO_LEFT_NEAR 5.237086 + +#define WALKX_RIGHT_FAR 3687.571777 +#define WALKY_RIGHT_FAR -5126.831055 +#define WALKZ_RIGHT_FAR 142.017807 +#define WALKO_RIGHT_FAR 0.604023 + +#define WALKX_RIGHT_MIDDLE 3707.990733 +#define WALKY_RIGHT_MIDDLE -5151.450195 +#define WALKZ_RIGHT_MIDDLE 142.032562 +#define WALKO_RIGHT_MIDDLE 1.376855 + +#define WALKX_RIGHT_NEAR 3739.500000 +#define WALKY_RIGHT_NEAR -5141.883989 +#define WALKZ_RIGHT_NEAR 142.0141130 +#define WALKO_RIGHT_NEAR 2.121412 + +//spells to be casted +#define SPELL_FROST_BOLT 28478 +#define SPELL_FROST_BOLT_NOVA 28479 +#define SPELL_CHAINS_OF_KELTHUZAD 28410 +#define SPELL_MANA_DETONATION 27819 +#define SPELL_SHADOW_FISURE 27810 +#define SPELL_FROST_BLAST 27808 + +//On Aggro +#define SAY_ARRIVAL1 "PRAY FOR MERCY!" +#define SOUND_ARRIVAL1 8809 +#define SAY_ARRIVAL3 "SCREAM YOR DYING BREATH!" +#define SOUND_ARRIVAL3 8810 +#define SAY_ARRIVAL5 "THE END IS UPON YOU!" +#define SOUND_ARRIVAL5 8811 + +//On Summon +#define SAY_REINFORCEMENTS1 "MINIONS, SERVANTS, SOLDIERS OF THE COLD DARK, OBEY THE CALL OF KEL'THUZAD!" +#define SOUND_REINFORCEMENTS1 8819 +#define SAY_REINFORCEMENTS2 "MASTER, I REQUIRE AID!" +#define SOUND_REINFORCEMENTS2 8816 +#define SAY_LICH_NAXX_SUMMON "VERY WELL. WARRIORS OF THE FROZEN WASTES RISE UP!. I COMMAND YOU TO FIGHT, KILL AND DIE AND DIE FOR YOUR MASTER! LET NONE SURVIVE!" +#define SOUND_LICH_NAXX_SUMMON 8824 + +//Random 1/4 taunt said when player enters 300 yards +#define SAY_TAUNT01 "WHO DARES VIOLATE THE SACTITY OF MY DOMAIN? BE WARNED, ALL WHO TRASPASS HERE ARE DOOMED" +#define SOUND_TAUNT01 8820 +#define SAY_TAUNT02 "FOOLS, YOU THINK YOURSELVES TRIUMPHANT? YOU HAVE ONLY TAKEN ONE STEP CLOSER TO THE ABYSS!" +#define SOUND_TAUNT02 8821 +#define SAY_TAUNT03 "I GROW TIRED OF THESE GAMES. PROCEED, AND I WILL BANISH YOUR SOULS TO OBLIVION!" +#define SOUND_TAUNT03 8822 +#define SAY_TAUNT04 "YOU HAVE NO IDEA WHAT HORRORS LIE AHEAD. YOU HAVE SEEN NOTHING! THE FROZEN HEART OF NAXXRAMAS AWAITS YOU!" +#define SOUND_TAUNT04 8823 + +//On kill unit +#define SAY_SLAY "THE DARK VOID AWAITS YOU!" +#define SOUND_SLAY 8817 + +//Specials +#define SAY_FROST "I WILL FREEZE THE BLOOD IN YOUR VEINS!" +#define SOUND_FROST 8815 +#define SAY_CHAIN1 "YOUR SOUL IS BOUND TO ME NOW!" +#define SOUND_CHAIN1 8812 +#define SAY_CHAIN2 "THERE WILL BE NO ESCAPE!" +#define SOUND_CHAIN2 8813 +#define SAY_SPECIAL1 "YOUR PETTY MAGICS ARE NO CHALLENGE TO THE MIGTH OF THE SCOURGE" +#define SOUND_SPECIAL1 9088 +#define SAY_SPECIAL2 "ENOUGH! I GROW TIRED OF THESE DISTRACTIONS!" +#define SOUND_SPECIAL2 9090 +#define SAY_DISPEL "FOOLS, YOU HAVE SPREAD YOUR POWERS TOO THIN. BE FREE, MY MINIONS!" +#define SOUND_DISPEL 9089 + +//On death +#define SAY_DEATH "DO NOT REJOICE... YOUR VICTORY IS A HOLLOW ONE... FOR I SHALL RETURN WITH POWERS BEYOND YOUR IMAGINING!" +#define SOUND_DEATH 8814 + +struct MANGOS_DLL_DECL boss_kelthuzadAI : public ScriptedAI +{ + boss_kelthuzadAI(Creature* c) : ScriptedAI(c) + { + GuardiansOfIcecrown[0] = 0; + GuardiansOfIcecrown[1] = 0; + GuardiansOfIcecrown[2] = 0; + GuardiansOfIcecrown[3] = 0; + GuardiansOfIcecrown[4] = 0; + GuardiansOfIcecrown_Count = 0; + Reset(); + } + + uint64 GuardiansOfIcecrown[5]; + uint32 GuardiansOfIcecrown_Count; + uint32 GuardiansOfIcecrown_Timer; + uint32 FrostBolt_Timer; + uint32 FrostBoltNova_Timer; + uint32 ChainsOfKelthuzad_Timer; + uint32 ManaDetonation_Timer; + uint32 ShadowFisure_Timer; + uint32 FrostBlast_Timer; + uint32 ChainsOfKelthuzad_Targets; + uint32 Phase1_Timer; + bool Phase2; + bool Phase3; + + void Reset() + { + FrostBolt_Timer = (rand()%60)*1000; //It won't be more than a minute without cast it + FrostBoltNova_Timer = 15000; //Cast every 15 seconds + ChainsOfKelthuzad_Timer = (rand()%30+30)*1000; //Cast no sooner than once every 30 seconds + ManaDetonation_Timer = 20000; //Seems to cast about every 20 seconds + ShadowFisure_Timer = 25000; //25 seconds + FrostBlast_Timer = (rand()%30+30)*1000; //Random time between 30-60 seconds + GuardiansOfIcecrown_Timer = 5000; //5 seconds for summoning each Guardian of Icecrown in phase 3 + + for(int i=0; i<5; i++) + if(GuardiansOfIcecrown[i]) + { + //delete creature + Unit* pUnit = Unit::GetUnit((*m_creature), GuardiansOfIcecrown[i]); + if (pUnit && pUnit->isAlive()) + pUnit->DealDamage(pUnit, pUnit->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + GuardiansOfIcecrown[i] = 0; + } + + Phase1_Timer = 310000; //Phase 1 lasts 5 minutes and 10 seconds + Phase2 = false; + Phase3 = false; + } + + void KilledUnit() + { + if (rand()%5) + return; + + DoYell(SAY_SLAY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + for(int i=0; i<5; i++) + if(GuardiansOfIcecrown[i]) + { + Unit* pUnit = Unit::GetUnit((*m_creature), GuardiansOfIcecrown[i]); + if (!pUnit || !pUnit->isAlive()) + continue; + + pUnit->CombatStop(); + float Walk_Pos_X; + float Walk_Pos_Y; + float Walk_Pos_Z; + switch(rand()%6) + { + case 0: + Walk_Pos_X = ADDX_LEFT_FAR; + Walk_Pos_Y = ADDY_LEFT_FAR; + Walk_Pos_Z = ADDZ_LEFT_FAR; + break; + case 1: + Walk_Pos_X = ADDX_LEFT_MIDDLE; + Walk_Pos_Y = ADDY_LEFT_MIDDLE; + Walk_Pos_Z = ADDZ_LEFT_MIDDLE; + break; + case 2: + Walk_Pos_X = ADDX_LEFT_NEAR; + Walk_Pos_Y = ADDY_LEFT_NEAR; + Walk_Pos_Z = ADDZ_LEFT_NEAR; + break; + case 3: + Walk_Pos_X = ADDX_RIGHT_FAR; + Walk_Pos_Y = ADDY_RIGHT_FAR; + Walk_Pos_Z = ADDZ_RIGHT_FAR; + break; + case 4: + Walk_Pos_X = ADDX_RIGHT_MIDDLE; + Walk_Pos_Y = ADDY_RIGHT_MIDDLE; + Walk_Pos_Z = ADDZ_RIGHT_MIDDLE; + break; + case 5: + Walk_Pos_X = ADDX_RIGHT_NEAR; + Walk_Pos_Y = ADDY_RIGHT_NEAR; + Walk_Pos_Z = ADDZ_RIGHT_NEAR; + break; + } + pUnit->SendMonsterMoveWithSpeed(Walk_Pos_X, Walk_Pos_Y, Walk_Pos_Z,MOVEMENTFLAG_WALK_MODE); + } + } + + void SayInitialAggro() //randomly select 1 of 3 say for aggro + { + switch(rand()%3) + { + case 0: + DoYell(SAY_ARRIVAL1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_ARRIVAL1); + break; + case 1: + DoYell(SAY_ARRIVAL3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_ARRIVAL3); + break; + case 2: + DoYell(SAY_ARRIVAL5,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_ARRIVAL5); + break; + } + } + + void Aggro(Unit* who) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_ARRIVAL1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_ARRIVAL1); + break; + case 1: + DoYell(SAY_ARRIVAL3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_ARRIVAL3); + break; + case 2: + DoYell(SAY_ARRIVAL5,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_ARRIVAL5); + break; + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget()) + return; + + if(m_creature->getVictim() && m_creature->isAlive()) + { + //Check for Frost Bolt + if(FrostBolt_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROST_BOLT); + + if(rand()%2 == 0) + { + DoYell(SAY_FROST,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_FROST); + } + //Cast again on time + FrostBolt_Timer = (rand()%60)*1000; + } + else FrostBolt_Timer -= diff; + + //Check for Frost Bolt Nova + if(FrostBoltNova_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROST_BOLT_NOVA); + + if(rand()%2 == 0) + { + DoYell(SAY_FROST,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_FROST); + } + + FrostBoltNova_Timer = 15000; + } + else FrostBoltNova_Timer -= diff; + + //Check for Chains Of Kelthuzad + if(ChainsOfKelthuzad_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CHAINS_OF_KELTHUZAD); + + if(rand()%2 == 0) + if(rand()%2 == 0) + { + DoYell(SAY_CHAIN1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_CHAIN1); + } + else + { + DoYell(SAY_CHAIN2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_CHAIN2); + } + + //cast again on time + ChainsOfKelthuzad_Timer = (rand()%30+30)*1000; + } + else ChainsOfKelthuzad_Timer -= diff; + + //Check for Mana Detonation + if(ManaDetonation_Timer < diff) + { + //time to cast + //DoCast(m_creature->getVictim(),SPELL_MANA_DETONATION); + + if(rand()%2 == 0) + { + DoYell(SAY_SPECIAL1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SPECIAL1); + } + ManaDetonation_Timer = 20000; + } + else ManaDetonation_Timer -= diff; + + //Check for Shadow Fissure + if(ShadowFisure_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOW_FISURE); + + if(rand()%2 == 0) + { + DoYell(SAY_SPECIAL2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SPECIAL2); + } + + ShadowFisure_Timer = 25000; + } + else ShadowFisure_Timer -= diff; + + //Check for Frost Blast + if(FrostBlast_Timer < diff) + { + //time to cast + //DoCast(m_creature->getVictim(),SPELL_FROST_BLAST); + + if(rand()%2 == 0) + { + DoYell(SAY_FROST,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_FROST); + } + + FrostBlast_Timer = (rand()%30+30)*1000; + } + else FrostBlast_Timer -= diff; + + //start phase 3 when we are 40% health + if(!Phase3 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 40) + { + Phase3 = true; + switch(rand()%2) + { + case 1: + DoYell(SAY_REINFORCEMENTS1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_REINFORCEMENTS1); + break; + case 2: + DoYell(SAY_REINFORCEMENTS2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_REINFORCEMENTS2); + break; + } + //here Lich King should respond to KelThuzad but I don't know which creature to make talk + //so for now just make Kelthuzad says it. + DoPlaySoundToSet(m_creature,SOUND_LICH_NAXX_SUMMON); + } + + if(Phase3 && (GuardiansOfIcecrown_Count < 5)) + if(GuardiansOfIcecrown_Timer < diff) + { + //Summon a Guardian of Icecrown in a random alcove (Creature # 16441) + //uint32 TimeToWalk; + Unit* pUnit = NULL; + float Walk_Pos_X; + float Walk_Pos_Y; + float Walk_Pos_Z; + switch(rand()%6) + { + case 0: + pUnit = m_creature->SummonCreature(16441,ADDX_LEFT_FAR,ADDY_LEFT_FAR,ADDZ_LEFT_FAR,ADDO_LEFT_FAR,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,1000); + //Setting walk position + Walk_Pos_X = WALKX_LEFT_FAR; + Walk_Pos_Y = WALKY_LEFT_FAR; + Walk_Pos_Z = WALKZ_LEFT_FAR; + break; + case 1: + pUnit = m_creature->SummonCreature(16441,ADDX_LEFT_MIDDLE,ADDY_LEFT_MIDDLE,ADDZ_LEFT_MIDDLE,ADDO_LEFT_MIDDLE,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,1000); + //Start moving guardian towards the center of the room + Walk_Pos_X = WALKX_LEFT_MIDDLE; + Walk_Pos_Y = WALKY_LEFT_MIDDLE; + Walk_Pos_Z = WALKZ_LEFT_MIDDLE; + break; + case 2: + pUnit = m_creature->SummonCreature(16441,ADDX_LEFT_NEAR,ADDY_LEFT_NEAR,ADDZ_LEFT_NEAR,ADDO_LEFT_NEAR,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,1000); + //Start moving guardian towards the center of the room + Walk_Pos_X = WALKX_LEFT_NEAR; + Walk_Pos_Y = WALKY_LEFT_NEAR; + Walk_Pos_Z = WALKZ_LEFT_NEAR; + break; + case 3: + + pUnit = m_creature->SummonCreature(16441,ADDX_RIGHT_FAR,ADDY_RIGHT_FAR,ADDZ_RIGHT_FAR,ADDO_RIGHT_FAR,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,1000); + //Start moving guardian towards the center of the room + Walk_Pos_X = WALKX_RIGHT_FAR; + Walk_Pos_Y = WALKY_RIGHT_FAR; + Walk_Pos_Z = WALKZ_RIGHT_FAR; + break; + case 4: + pUnit = m_creature->SummonCreature(16441,ADDX_RIGHT_MIDDLE,ADDY_RIGHT_MIDDLE,ADDZ_RIGHT_MIDDLE,ADDO_RIGHT_MIDDLE,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,1000); + //Start moving guardian towards the center of the room + Walk_Pos_X = WALKX_RIGHT_MIDDLE; + Walk_Pos_Y = WALKY_RIGHT_MIDDLE; + Walk_Pos_Z = WALKZ_RIGHT_MIDDLE; + break; + case 5: + pUnit = m_creature->SummonCreature(16441,ADDX_RIGHT_NEAR,ADDY_RIGHT_NEAR,ADDZ_RIGHT_NEAR,ADDO_RIGHT_NEAR,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,1000); + //Start moving guardian towards the center of the room + Walk_Pos_X = WALKX_RIGHT_NEAR; + Walk_Pos_Y = WALKY_RIGHT_NEAR; + Walk_Pos_Z = WALKZ_RIGHT_NEAR; + break; + } + + if (pUnit) + { + //if we find no one to figth walk to the center + if(!pUnit->isInCombat()) + pUnit->SendMonsterMoveWithSpeed(Walk_Pos_X,Walk_Pos_Y,Walk_Pos_Z,MOVEMENTFLAG_WALK_MODE); + + //Safe storing of creatures + GuardiansOfIcecrown[GuardiansOfIcecrown_Count] = pUnit->GetGUID(); + + //Update guardian count + GuardiansOfIcecrown_Count++; + + } + //5 seconds until summoning next guardian + GuardiansOfIcecrown_Timer = 5000; + } + else GuardiansOfIcecrown_Timer -= diff; + + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_boss_kelthuzadAI(Creature *_Creature) +{ + return new boss_kelthuzadAI (_Creature); +} + +void AddSC_boss_kelthuzad() +{ + //This script is disabled until it has been throughly tested + + /* + Script *newscript; + newscript = new Script; + newscript->Name="boss_kelthuzad"; + newscript->GetAI = GetAI_boss_kelthuzadAI; + m_scripts[nrscripts++] = newscript; + */ +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_lady_blaumeux.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_lady_blaumeux.cpp new file mode 100644 index 00000000000..6946e962aae --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_lady_blaumeux.cpp @@ -0,0 +1,147 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Lady_Blaumeux +SD%Complete: 100 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +//All horsemen +#define SPELL_SHIELDWALL 29061 +#define SPELL_BESERK 26662 + +// lady blaumeux +#define SPELL_MARK_OF_BLAUMEUX 28833 +#define SPELL_VOIDZONE 28863 + +#define SAY_AGGRO "Defend youself!" +#define SAY_TAUNT1 "Come, Zeliek, do not drive them out. Not before we've had our fun." +#define SAY_TAUNT2 "I do hope they stay alive long enough for me to... introduce myself." +#define SAY_TAUNT3 "The first kill goes to me! Anyone care to wager?" +#define SAY_SPECIAL "Your life is mine!" +#define SAY_SLAY "Who's next?" +#define SAY_DEATH "Tou... che!" + +#define SOUND_AGGRO 8892 +#define SOUND_TAUNT1 8896 +#define SOUND_TAUNT2 8897 +#define SOUND_TAUNT3 8898 +#define SOUND_SPECIAL 8895 +#define SOUND_SLAY 8894 +#define SOUND_DEATH 8893 + +#define SPIRIT_OF_BLAUMEUX 16776 + +struct MANGOS_DLL_DECL boss_lady_blaumeuxAI : public ScriptedAI +{ + boss_lady_blaumeuxAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Mark_Timer; + uint32 VoidZone_Timer; + bool ShieldWall1; + bool ShieldWall2; + + void Reset() + { + Mark_Timer = 20000; // First Horsemen Mark is applied at 20 sec. + VoidZone_Timer = 12000; // right + ShieldWall1 = true; + ShieldWall2 = true; + } + + void InitialYell() + { + if(!InCombat) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + } + + void KilledUnit() + { + DoYell(SAY_SLAY,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + void Aggro(Unit *who) + { + InitialYell(); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + // Mark of Blaumeux + if(Mark_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MARK_OF_BLAUMEUX); + Mark_Timer = 12000; + }else Mark_Timer -= diff; + + // Shield Wall - All 4 horsemen will shield wall at 50% hp and 20% hp for 20 seconds + if(ShieldWall1 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 50) + { + if(ShieldWall1) + { + DoCast(m_creature,SPELL_SHIELDWALL); + ShieldWall1 = false; + } + } + if(ShieldWall2 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20) + { + if(ShieldWall2) + { + DoCast(m_creature,SPELL_SHIELDWALL); + ShieldWall2 = false; + } + } + + // Void Zone + if(VoidZone_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_VOIDZONE); + VoidZone_Timer = 12000; + }else VoidZone_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_lady_blaumeux(Creature *_Creature) +{ + return new boss_lady_blaumeuxAI (_Creature); +} + +void AddSC_boss_lady_blaumeux() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_lady_blaumeux"; + newscript->GetAI = GetAI_boss_lady_blaumeux; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_loatheb.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_loatheb.cpp new file mode 100644 index 00000000000..cc4390ceb8e --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_loatheb.cpp @@ -0,0 +1,216 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Loatheb +SD%Complete: 100 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +#define SAY_AGGRO1 "You are mine now!" +#define SAY_AGGRO2 "I see you!" +#define SAY_AGGRO3 "You...are next!" +#define SAY_SLAY1 "Close your eyes... sleep!" +#define SAY_SLAY2 "The races of the world will perish. It is only a matter of time." +#define SAY_SLAY3 "I see endless suffering, I see torment, I see rage. I see... everything!" +#define SAY_SLAY4 "Soon... the world will tremble!" +#define SAY_SLAY5 "The end is upon you." +#define SAY_SLAY6 "Hungry worms will feast on your rotten flesh!" +#define SAY_DEATH "" + +#define SOUND_AGGRO1 8825 +#define SOUND_AGGRO2 8826 +#define SOUND_AGGRO3 8827 +#define SOUND_SLAY1 8829 +#define SOUND_SLAY2 8830 +#define SOUND_SLAY3 8831 +#define SOUND_SLAY4 8832 +#define SOUND_SLAY5 8833 +#define SOUND_SLAY6 8834 +#define SOUND_DEATH 8828 + +#define SPELL_CORRUPTED_MIND 29198 +#define SPELL_POISON_AURA 29865 +#define SPELL_INEVITABLE_DOOM 29204 +#define SPELL_REMOVE_CURSE 30281 + +#define ADD_1X 2957.040 +#define ADD_1Y -3997.590 +#define ADD_1Z 274.280 + +#define ADD_2X 2909.130 +#define ADD_2Y -4042.970 +#define ADD_2Z 274.280 + +#define ADD_3X 2861.102 +#define ADD_3Y -3997.901 +#define ADD_3Z 274.280 + +struct MANGOS_DLL_DECL boss_loathebAI : public ScriptedAI +{ + boss_loathebAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 CorruptedMind_Timer; + uint32 PoisonAura_Timer; + uint32 InevitableDoom_Timer; + uint32 InevitableDoom5mins_Timer; + uint32 RemoveCurse_Timer; + uint32 Summon_Timer; + + void Reset() + { + CorruptedMind_Timer = 4000; + PoisonAura_Timer = 2500; + InevitableDoom_Timer = 120000; + InevitableDoom5mins_Timer = 300000; + RemoveCurse_Timer = 30000; + Summon_Timer = 8000; + } + + void Aggro(Unit *who) + { + switch (rand()%3) + { + case 0: + DoYell(SAY_AGGRO1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO3); + break; + } + } + + void KilledUnit(Unit* victim) + { + switch (rand()%6) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY2); + break; + case 2: + DoYell(SAY_SLAY3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY3); + break; + case 3: + DoYell(SAY_SLAY4,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY4); + break; + case 4: + DoYell(SAY_SLAY5,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY5); + break; + case 5: + DoYell(SAY_SLAY6,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY6); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //CorruptedMind_Timer + if (CorruptedMind_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CORRUPTED_MIND); + CorruptedMind_Timer = 62000; + }else CorruptedMind_Timer -= diff; + + //PoisonAura_Timer + if (PoisonAura_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_POISON_AURA); + PoisonAura_Timer = 60000; + }else PoisonAura_Timer -= diff; + + //InevitableDoom_Timer + if (InevitableDoom_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_INEVITABLE_DOOM); + InevitableDoom_Timer = 120000; + }else InevitableDoom_Timer -= diff; + + //InevitableDoom5mins_Timer + if (InevitableDoom5mins_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_INEVITABLE_DOOM); + InevitableDoom5mins_Timer = 15000; + }else InevitableDoom5mins_Timer -= diff; + + //RemoveCurse_Timer + if (RemoveCurse_Timer < diff) + { + DoCast(m_creature,SPELL_REMOVE_CURSE); + RemoveCurse_Timer = 30000; + }else RemoveCurse_Timer -= diff; + + //Summon_Timer + if (Summon_Timer < diff) + { + Unit* target = NULL; + Unit* SummonedSpores = NULL; + + SummonedSpores = m_creature->SummonCreature(16286,ADD_1X,ADD_1Y,ADD_1Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedSpores = m_creature->SummonCreature(16286,ADD_2X,ADD_2Y,ADD_2Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedSpores = m_creature->SummonCreature(16286,ADD_3X,ADD_3Y,ADD_3Z,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + if (SummonedSpores) + { + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + SummonedSpores->AddThreat(target,1.0f); + } + + Summon_Timer = 28000; + } else Summon_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_loatheb(Creature *_Creature) +{ + return new boss_loathebAI (_Creature); +} + +void AddSC_boss_loatheb() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_loatheb"; + newscript->GetAI = GetAI_boss_loatheb; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_maexxna.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_maexxna.cpp new file mode 100644 index 00000000000..bd10cff23a0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_maexxna.cpp @@ -0,0 +1,246 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Maexxna +SD%Complete: 80 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_WEBTRAP 28622 //Spell is normally used by the webtrap on the wall NOT by Maexxna +#define SPELL_WEBSPRAY 29484 +#define SPELL_POISONSHOCK 28741 +#define SPELL_NECROTICPOISON 28776 +#define SPELL_ENRAGE 28747 +#define SPELL_SUMMON_SPIDERLING 29434 + +#define LOC_X1 3546.796 +#define LOC_Y1 -3869.082 +#define LOC_Z1 296.450 + +#define LOC_X2 3531.271 +#define LOC_Y2 -3847.424 +#define LOC_Z2 299.450 + +#define LOC_X3 3497.067 +#define LOC_Y3 -3843.384 +#define LOC_Z3 302.384 + +struct MANGOS_DLL_DECL mob_webwrapAI : public ScriptedAI +{ + mob_webwrapAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint64 victimGUID; + + void Reset() + { + victimGUID = 0; + } + + void SetVictim(Unit* victim) + { + if(victim) + { + victimGUID = victim->GetGUID(); + victim->CastSpell(victim, SPELL_WEBTRAP, true); + } + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if(damage > m_creature->GetHealth()) + { + if(victimGUID) + { + Unit* victim = NULL; + victim = Unit::GetUnit((*m_creature), victimGUID); + victim->RemoveAurasDueToSpell(SPELL_WEBTRAP); + } + } + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + } +}; + +struct MANGOS_DLL_DECL boss_maexxnaAI : public ScriptedAI +{ + boss_maexxnaAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 WebTrap_Timer; + uint32 WebSpray_Timer; + uint32 PoisonShock_Timer; + uint32 NecroticPoison_Timer; + uint32 SummonSpiderling_Timer; + bool Enraged; + + void Reset() + { + WebTrap_Timer = 20000; //20 sec init, 40 sec normal + WebSpray_Timer = 40000; //40 seconds + PoisonShock_Timer = 20000; //20 seconds + NecroticPoison_Timer = 30000; //30 seconds + SummonSpiderling_Timer = 30000; //30 sec init, 40 sec normal + Enraged = false; + } + + void Aggro(Unit *who) + { + } + + void DoCastWebWrap() + { + std::list t_list = m_creature->getThreatManager().getThreatList(); + std::vector targets; + + //This spell doesn't work if we only have 1 player on threat list + if(t_list.size() < 2) + return; + + //begin + 1 , so we don't target the one with the highest threat + std::list::iterator itr = t_list.begin(); + std::advance(itr, 1); + for( ; itr!= t_list.end(); ++itr) //store the threat list in a different container + { + Unit *target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + //only on alive players + if(target && target->isAlive() && target->GetTypeId() == TYPEID_PLAYER ) + targets.push_back( target); + } + + while(targets.size() > 3) + //cut down to size if we have more than 3 targets + targets.erase(targets.begin()+rand()%targets.size()); + + int i = 0; + for(std::vector::iterator itr = targets.begin(); itr!= targets.end(); ++itr, ++i) + { + // Teleport the 3 targets to a location on the wall and summon a Web Wrap on them + Unit *target = *itr; + Creature* Wrap = NULL; + if(target) + { + switch(i) + { + case 0: + DoTeleportPlayer(target, LOC_X1, LOC_Y1, LOC_Z1, target->GetOrientation()); + Wrap = m_creature->SummonCreature(16486, LOC_X1, LOC_Y1, LOC_Z1, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000); + break; + case 1: + DoTeleportPlayer(target, LOC_X2, LOC_Y2, LOC_Z2, target->GetOrientation()); + Wrap = m_creature->SummonCreature(16486, LOC_X2, LOC_Y2, LOC_Z2, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000); + break; + case 2: + DoTeleportPlayer(target, LOC_X3, LOC_Y3, LOC_Z3, target->GetOrientation()); + Wrap = m_creature->SummonCreature(16486, LOC_X3, LOC_Y3, LOC_Z3, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000); + break; + } + if(Wrap) + { + Wrap->setFaction(m_creature->getFaction()); + ((mob_webwrapAI*)Wrap->AI())->SetVictim(target); + } + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //WebTrap_Timer + if (WebTrap_Timer < diff) + { + DoCastWebWrap(); + WebTrap_Timer = 40000; + }else WebTrap_Timer -= diff; + + //WebSpray_Timer + if (WebSpray_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_WEBSPRAY); + WebSpray_Timer = 40000; + }else WebSpray_Timer -= diff; + + //PoisonShock_Timer + if (PoisonShock_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_POISONSHOCK); + PoisonShock_Timer = 20000; + }else PoisonShock_Timer -= diff; + + //NecroticPoison_Timer + if (NecroticPoison_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_NECROTICPOISON); + NecroticPoison_Timer = 30000; + }else NecroticPoison_Timer -= diff; + + //SummonSpiderling_Timer + if (SummonSpiderling_Timer < diff) + { + DoCast(m_creature, SPELL_SUMMON_SPIDERLING); + SummonSpiderling_Timer = 40000; + }else SummonSpiderling_Timer -= diff; + + //Enrage if not already enraged and below 30% + if (!Enraged && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 30) + { + DoCast(m_creature,SPELL_ENRAGE); + Enraged = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_webwrap(Creature* _Creature) +{ + return new mob_webwrapAI (_Creature); +} + +CreatureAI* GetAI_boss_maexxna(Creature *_Creature) +{ + return new boss_maexxnaAI (_Creature); +} + +void AddSC_boss_maexxna() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_maexxna"; + newscript->GetAI = GetAI_boss_maexxna; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_webwrap"; + newscript->GetAI = GetAI_mob_webwrap; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_noth.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_noth.cpp new file mode 100644 index 00000000000..1682344bf91 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_noth.cpp @@ -0,0 +1,180 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Noth +SD%Complete: 40 +SDComment: Missing Balcony stage +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +#define SAY_AGGRO1 "Glory to the master!" +#define SAY_AGGRO2 "Your life is forfeit!" +#define SAY_AGGRO3 "Die, trespasser!" +#define SAY_SUMMON "Rise, my soldiers! Rise and fight once more!" +#define SAY_SLAY1 "My task is done!" +#define SAY_SLAY2 "Breathe no more!" +#define SAY_DEATH "I will serve the master... in... death!" +#define SOUND_AGGRO1 8845 +#define SOUND_AGGRO2 8846 +#define SOUND_AGGRO3 8847 +#define SOUND_SUMMON 8851 +#define SOUND_SLAY1 8849 +#define SOUND_SLAY2 8850 +#define SOUND_DEATH 8848 + +// Teleport position of Noth on his balcony +#define TELE_X 2631.370 +#define TELE_Y -3529.680 +#define TELE_Z 274.040 +#define TELE_O 6.277 + +#define SPELL_BLINK 29211 +#define SPELL_CRIPPLE 29212 +#define SPELL_CURSEPLAGUEBRINGER 28213 +#define SPELL_WRATHPLAGUEBRINGER 28214 + +// IMPORTANT: BALCONY TELEPORT NOT ADDED YET! WILL BE ADDED SOON! + +struct MANGOS_DLL_DECL boss_nothAI : public ScriptedAI +{ + boss_nothAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Blink_Timer; + uint32 Curse_Timer; + uint32 Wrath_Timer; + uint32 Summon_Timer; + + void Reset() + { + Blink_Timer = 25000; + Curse_Timer = 4000; + Wrath_Timer = 9000; + Summon_Timer = 12000; + } + + void Aggro(Unit *who) + { + switch (rand()%3) + { + case 0: + DoYell(SAY_AGGRO1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO1); + break; + case 1: + DoYell(SAY_AGGRO2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO2); + break; + case 2: + DoYell(SAY_AGGRO3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO3); + break; + } + } + + void KilledUnit(Unit* victim) + { + switch (rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Blink_Timer + if (Blink_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CRIPPLE); + DoCast(m_creature,SPELL_BLINK); + + Blink_Timer = 25000; + }else Blink_Timer -= diff; + + //Curse_Timer + if (Curse_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CURSEPLAGUEBRINGER); + Curse_Timer = 28000; + }else Curse_Timer -= diff; + + //Wrath_Timer + if (Wrath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_WRATHPLAGUEBRINGER); + Wrath_Timer = 18000; + }else Wrath_Timer -= diff; + + //Summon_Timer + if (Summon_Timer < diff) + { + DoYell(SAY_SUMMON,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SUMMON); + + Unit* target = NULL; + Unit* SummonedSkeletons = NULL; + + SummonedSkeletons = m_creature->SummonCreature(16984,2684.804,-3502.517,261.313,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedSkeletons = m_creature->SummonCreature(16984,2684.804,-3502.517,261.313,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedSkeletons = m_creature->SummonCreature(16984,2684.804,-3502.517,261.313,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedSkeletons = m_creature->SummonCreature(16984,2684.804,-3502.517,261.313,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedSkeletons = m_creature->SummonCreature(16984,2684.804,-3502.517,261.313,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + SummonedSkeletons = m_creature->SummonCreature(16984,2684.804,-3502.517,261.313,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,80000); + + if (SummonedSkeletons) + { + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + SummonedSkeletons->AddThreat(target,1.0f); + } + + Summon_Timer = 30500; + } else Summon_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_noth(Creature *_Creature) +{ + return new boss_nothAI (_Creature); +} + +void AddSC_boss_noth() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_noth"; + newscript->GetAI = GetAI_boss_noth; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_patchwerk.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_patchwerk.cpp new file mode 100644 index 00000000000..d8a8f73fd69 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_patchwerk.cpp @@ -0,0 +1,160 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Patchwerk +SD%Complete: 100 +SDComment: Some issues with hateful strike inturrupting the melee swing timer. Probably core issue. +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +#define SAY_AGGRO1 "Patchwerk want to play!" +#define SAY_AGGRO2 "Kel'Thuzad make Patchwerk his Avatar of War!" +#define SAY_SLAY "No more play?" +#define SAY_DEATH "What happened to... Patch..." + +#define SOUND_AGGRO1 8909 +#define SOUND_AGGRO2 8910 +#define SOUND_SLAY 8912 +#define SOUND_DEATH 8911 + +#define EMOTE_BERSERK "Patchwerk goes into a berserker rage!" +#define EMOTE_ENRAGE "Patchwerk becomes enraged!" + +#define SPELL_HATEFULSTRIKE 28308 +#define SPELL_ENRAGE 29691 +#define SPELL_BERSERK 27680 +#define SPELL_SLIMEBOLT 32309 + +struct MANGOS_DLL_DECL boss_patchwerkAI : public ScriptedAI +{ + boss_patchwerkAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 HatefullStrike_Timer; + uint32 Enrage_Timer; + uint32 Slimebolt_Timer; + bool Enraged; + + void Reset() + { + HatefullStrike_Timer = 1200; //1.2 seconds + Enrage_Timer = 420000; //7 minutes 420,000 + Slimebolt_Timer = 450000; //7.5 minutes 450,000 + Enraged = false; + } + + void KilledUnit(Unit* Victim) + { + if (rand()%5) + return; + + DoYell(SAY_SLAY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void Aggro(Unit *who) + { + if (rand()%2) + { + DoYell(SAY_AGGRO1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + } + else + { + DoYell(SAY_AGGRO2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //HatefullStrike_Timer + if (HatefullStrike_Timer < diff) + { + //Cast Hateful strike on the player with the highest + //amount of HP within melee distance + uint32 MostHP = 0; + Unit* pMostHPTarget = NULL; + Unit* pTemp = NULL; + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + + for (i = m_creature->getThreatManager().getThreatList().begin(); i!=m_creature->getThreatManager().getThreatList().end(); ++i) + { + pTemp = Unit::GetUnit((*m_creature),(*i)->getUnitGuid()); + if (pTemp && pTemp->isAlive() && pTemp->GetHealth() > MostHP && m_creature->GetDistance2d(pTemp) < 5) + { + MostHP = pTemp->GetHealth(); + pMostHPTarget = pTemp; + } + } + + if (pMostHPTarget) + DoCast(pMostHPTarget, SPELL_HATEFULSTRIKE); + + HatefullStrike_Timer = 1200; + }else HatefullStrike_Timer -= diff; + + //Enrage_Timer + if (Enrage_Timer < diff) + { + DoCast(m_creature, SPELL_BERSERK); + DoTextEmote(EMOTE_BERSERK, m_creature->getVictim()); + + Enrage_Timer = 300000; + }else Enrage_Timer -= diff; + + //Slimebolt_Timer + if (Slimebolt_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SLIMEBOLT); + Slimebolt_Timer = 5000; + }else Slimebolt_Timer -= diff; + + //Enrage if not already enraged and below 5% + if (!Enraged && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 5) + { + DoCast(m_creature,SPELL_ENRAGE); + DoTextEmote(EMOTE_ENRAGE,NULL); + Enraged = true; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_patchwerk(Creature *_Creature) +{ + return new boss_patchwerkAI (_Creature); +} + +void AddSC_boss_patchwerk() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_patchwerk"; + newscript->GetAI = GetAI_boss_patchwerk; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_razuvious.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_razuvious.cpp new file mode 100644 index 00000000000..707ec574076 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_razuvious.cpp @@ -0,0 +1,167 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Razuvious +SD%Complete: 50 +SDComment: Missing adds and event is impossible without Mind Control +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +//Razuvious - NO TEXT sound only +//8852 aggro01 - Hah hah, I'm just getting warmed up! +//8853 aggro02 Stand and fight! +//8854 aggro03 Show me what you've got! +//8861 slay1 - You should've stayed home! +//8863 slay2- +//8858 cmmnd3 - You disappoint me, students! +//8855 cmmnd1 - Do as I taught you! +//8856 cmmnd2 - Show them no mercy! +//8859 cmmnd4 - The time for practice is over! Show me what you've learned! +//8861 Sweep the leg! Do you have a problem with that? +//8860 death - An honorable... death... +//8947 - Aggro Mixed? - ? + +#define SOUND_AGGRO1 8852 +#define SOUND_AGGRO2 8853 +#define SOUND_AGGRO3 8854 +#define SOUND_SLAY1 8861 +#define SOUND_SLAY2 8863 +#define SOUND_COMMND1 8855 +#define SOUND_COMMND2 8856 +#define SOUND_COMMND3 8858 +#define SOUND_COMMND4 8859 +#define SOUND_COMMND5 8861 +#define SOUND_DEATH 8860 +#define SOUND_AGGROMIX 8847 + +#define SPELL_UNBALANCINGSTRIKE 26613 +#define SPELL_DISRUPTINGSHOUT 29107 + +struct MANGOS_DLL_DECL boss_razuviousAI : public ScriptedAI +{ + boss_razuviousAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 UnbalancingStrike_Timer; + uint32 DisruptingShout_Timer; + uint32 CommandSound_Timer; + + void Reset() + { + UnbalancingStrike_Timer = 30000; //30 seconds + DisruptingShout_Timer = 25000; //25 seconds + CommandSound_Timer = 40000; //40 seconds + } + + void KilledUnit(Unit* Victim) + { + if (rand()%3) + return; + + switch (rand()%2) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void Aggro(Unit *who) + { + switch (rand()%3) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_AGGRO1); + break; + case 1: + DoPlaySoundToSet(m_creature, SOUND_AGGRO2); + break; + case 2: + DoPlaySoundToSet(m_creature, SOUND_AGGRO3); + break; + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //UnbalancingStrike_Timer + if (UnbalancingStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_UNBALANCINGSTRIKE); + UnbalancingStrike_Timer = 30000; + }else UnbalancingStrike_Timer -= diff; + + //DisruptingShout_Timer + if (DisruptingShout_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_DISRUPTINGSHOUT); + DisruptingShout_Timer = 25000; + }else DisruptingShout_Timer -= diff; + + //CommandSound_Timer + if (CommandSound_Timer < diff) + { + switch (rand()%5) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_COMMND1); + break; + case 1: + DoPlaySoundToSet(m_creature, SOUND_COMMND2); + break; + case 2: + DoPlaySoundToSet(m_creature, SOUND_COMMND3); + break; + case 3: + DoPlaySoundToSet(m_creature, SOUND_COMMND4); + break; + case 4: + DoPlaySoundToSet(m_creature, SOUND_COMMND5); + break; + } + + CommandSound_Timer = 40000; + }else CommandSound_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_razuvious(Creature *_Creature) +{ + return new boss_razuviousAI (_Creature); +} + +void AddSC_boss_razuvious() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_razuvious"; + newscript->GetAI = GetAI_boss_razuvious; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_sapphiron.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_sapphiron.cpp new file mode 100644 index 00000000000..f27375f2835 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_sapphiron.cpp @@ -0,0 +1,199 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Sapphiron +SD%Complete: 0 +SDComment: Place Holder +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_ICEBOLT 28522 +#define SPELL_FROST_BREATH 29318 +#define SPELL_FROST_AURA 28531 +#define SPELL_LIFE_DRAIN 28542 +#define SPELL_BLIZZARD 28547 +#define SPELL_BESERK 26662 + +struct MANGOS_DLL_DECL boss_sapphironAI : public ScriptedAI +{ + boss_sapphironAI(Creature* c) : ScriptedAI(c) + { + Reset(); + } + + uint32 Icebolt_Count; + uint32 Icebolt_Timer; + uint32 FrostBreath_Timer; + uint32 FrostAura_Timer; + uint32 LifeDrain_Timer; + uint32 Blizzard_Timer; + uint32 Fly_Timer; + uint32 Fly2_Timer; + uint32 Beserk_Timer; + uint32 phase; + bool landoff; + uint32 land_Timer; + + void Reset() + { + FrostAura_Timer = 2000; + LifeDrain_Timer = 24000; + Blizzard_Timer = 20000; + Fly_Timer = 45000; + Icebolt_Timer = 4000; + land_Timer = 2000; + Beserk_Timer = 0; + phase = 1; + Icebolt_Count = 0; + landoff = false; + + //m_creature->ApplySpellMod(SPELL_FROST_AURA, SPELLMOD_DURATION, -1); + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget()) + return; + + if(m_creature->getVictim() && m_creature->isAlive()) + { + if(phase == 1) + { + if(FrostAura_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROST_AURA); + FrostAura_Timer = 5000; + }else FrostAura_Timer -= diff; + + if(LifeDrain_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(target,SPELL_LIFE_DRAIN); + LifeDrain_Timer = 24000; + }else LifeDrain_Timer -= diff; + + if(Blizzard_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(target,SPELL_BLIZZARD); + Blizzard_Timer = 20000; + }else Blizzard_Timer -= diff; + + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() > 10) + { + if(Fly_Timer < diff) + { + phase = 2; + m_creature->InterruptNonMeleeSpells(false); + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + (*m_creature).GetMotionMaster()->Clear(false); + (*m_creature).GetMotionMaster()->MoveIdle(); + DoCast(m_creature,11010); + m_creature->SetHover(true); + DoCast(m_creature,18430); + Icebolt_Timer = 4000; + Icebolt_Count = 0; + landoff = false; + }else Fly_Timer -= diff; + } + + if (phase == 2) + { + if(Icebolt_Timer < diff && Icebolt_Count < 5) + { + Unit* target = NULL; + + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(target,SPELL_ICEBOLT); + Icebolt_Count ++; + Icebolt_Timer = 4000; + }else Icebolt_Timer -= diff; + + if(Icebolt_Count == 5 && !landoff) + { + if(FrostBreath_Timer < diff ) + { + DoTextEmote("takes a deep breath...",NULL); + DoCast(m_creature->getVictim(),SPELL_FROST_BREATH); + land_Timer = 2000; + landoff = true; + FrostBreath_Timer = 6000; + }else FrostBreath_Timer -= diff; + } + + if(landoff) + { + if(land_Timer < diff) + { + phase = 1; + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + m_creature->SetHover(false); + (*m_creature).GetMotionMaster()->Clear(false); + (*m_creature).GetMotionMaster()->MoveChase(m_creature->getVictim()); + Fly_Timer = 67000; + }else land_Timer -= diff; + } + + } + + if ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 11) + { + if (Beserk_Timer < diff) + { + DoTextEmote("enrages!",NULL); + DoCast(m_creature,SPELL_BESERK); + Beserk_Timer = 300000; + }else Beserk_Timer -= diff; + } + + if( phase!=2 && m_creature->getVictim() && m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + { + if( m_creature->isAttackReady() ) + { + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); + } + } + } + } + } +}; + +CreatureAI* GetAI_boss_sapphiron(Creature *_Creature) +{ + return new boss_sapphironAI (_Creature); +} + +void AddSC_boss_sapphiron() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_sapphiron"; + newscript->GetAI = GetAI_boss_sapphiron; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_sir_zeliek.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_sir_zeliek.cpp new file mode 100644 index 00000000000..717e84ca984 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_sir_zeliek.cpp @@ -0,0 +1,146 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Sir_Zeliek +SD%Complete: 100 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +//All horsemen +#define SPELL_SHIELDWALL 29061 +#define SPELL_BESERK 26662 + +// sir zeliek +#define SPELL_MARK_OF_ZELIEK 28835 +#define SPELL_HOLY_WRATH 28883 + +#define SAY_AGGRO "Flee, before it's too late!" +#define SAY_TAUNT1 "Invaders, cease this foolish venture at once! Turn away while you still can!" +#define SAY_TAUNT2 "Perhaps they will come to their senses, and run away as fast as they can!" +#define SAY_TAUNT3 "Do not continue! Turn back while there's still time!" +#define SAY_SPECIAL "I- I have no choice but to obey!" +#define SAY_SLAY "Forgive me!" +#define SAY_DEATH "It is... as it should be." + +#define SOUND_AGGRO 8913 +#define SOUND_TAUNT1 8917 +#define SOUND_TAUNT2 8918 +#define SOUND_TAUNT3 8919 +#define SOUND_SPECIAl 8916 +#define SOUND_SLAY 8915 +#define SOUND_DEATH 8914 + +struct MANGOS_DLL_DECL boss_sir_zeliekAI : public ScriptedAI +{ + boss_sir_zeliekAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Mark_Timer; + uint32 HolyWrath_Timer; + bool ShieldWall1; + bool ShieldWall2; + + void Reset() + { + Mark_Timer = 20000; // First Horsemen Mark is applied at 20 sec. + HolyWrath_Timer = 12000; // right + ShieldWall1 = true; + ShieldWall2 = true; + } + + void InitialYell() + { + if(!InCombat) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + } + + void KilledUnit() + { + DoYell(SAY_SLAY,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void Aggro(Unit *who) + { + InitialYell(); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + // Mark of Zeliek + if(Mark_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MARK_OF_ZELIEK); + Mark_Timer = 12000; + }else Mark_Timer -= diff; + + // Shield Wall - All 4 horsemen will shield wall at 50% hp and 20% hp for 20 seconds + if(ShieldWall1 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 50) + { + if(ShieldWall1) + { + DoCast(m_creature,SPELL_SHIELDWALL); + ShieldWall1 = false; + } + } + if(ShieldWall2 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20) + { + if(ShieldWall2) + { + DoCast(m_creature,SPELL_SHIELDWALL); + ShieldWall2 = false; + } + } + + // Holy Wrath + if(HolyWrath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HOLY_WRATH); + HolyWrath_Timer = 12000; + }else HolyWrath_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_sir_zeliek(Creature *_Creature) +{ + return new boss_sir_zeliekAI (_Creature); +} + +void AddSC_boss_sir_zeliek() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_sir_zeliek"; + newscript->GetAI = GetAI_boss_sir_zeliek; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_stalagg.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_stalagg.cpp new file mode 100644 index 00000000000..53b46122f24 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_stalagg.cpp @@ -0,0 +1,35 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Stalagg +SD%Complete: 0 +SDComment: Merge with Thaddius +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +//Stalagg +//8864 aggro - Stalagg crush you! +//8866 slay - Stalagg Kill! +//8865 death - Master save me... + +#define SPELL_WARSTOMP 28125 +#define SPELL_POWERSURGE 28134 +#define SPELL_CHAIN_LIGHTNING 28900 + +//Not sure how to "force" crushing blows or to knock tank to the opposite platform diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_thaddius.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_thaddius.cpp new file mode 100644 index 00000000000..0bd59cfa0e2 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_thaddius.cpp @@ -0,0 +1,49 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Thaddius +SD%Complete: 0 +SDComment: Merge Feugen & Stalagg with this script +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +//Thaddus +//8873 Lamnt01 - Pleeease! +//8874 Lamnt02 - Stop, make it stop! +//8875 Lamnt03 - Help me! Save me! +//8876 Lamnt04 - Please, nooo! +//8872 greet - You are too late... I... must... OBEY! +//8867 aggro1 - KILL! +//8868 aggro2 - EAT YOUR BONES! +//8869 aggro3 - BREAK YOU! +//8871 elect - Now YOU feel pain! +//8877 slay - You die now! +//8870 die - Thank... you... + +#define SPELL_BALL_LIGHTNING 28299 + +#define SPELL_CHARGE_POSITIVE_DMGBUFF 29659 +#define SPELL_CHARGE_POSITIVE_NEARDMG 28059 + +#define SPELL_CHARGE_NEGATIVE_DMGBUFF 29660 +#define SPELL_CHARGE_NEGATIVE_NEARDMG 28084 + +#define SPELL_CHAIN_LIGHTNING 28900 + +#define SPELL_BESERK 26662 diff --git a/src/bindings/scripts/scripts/zone/naxxramas/boss_thane_korthazz.cpp b/src/bindings/scripts/scripts/zone/naxxramas/boss_thane_korthazz.cpp new file mode 100644 index 00000000000..af2010fb3d9 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/boss_thane_korthazz.cpp @@ -0,0 +1,147 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Thane_Korthazz +SD%Complete: 100 +SDComment: +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" + +//All horsemen +#define SPELL_SHIELDWALL 29061 +#define SPELL_BESERK 26662 + +// thane korthazz +#define SPELL_MARK_OF_KORTHAZZ 28832 +#define SPELL_METEOR 26558 // m_creature->getVictim() auto-area spell but with a core problem + +#define SAY_AGGRO "Come out and fight, ye wee ninny!" +#define SAY_TAUNT1 "To arms, ye roustabouts! We've got company!" +#define SAY_TAUNT2 "I heard about enough of yer sniveling. Shut yer fly trap 'afore I shut it for ye!" +#define SAY_TAUNT3 "I'm gonna enjoy killin' these slack-jawed daffodils!" +#define SAY_SLAY "Next time, bring more friends!" +#define SAY_SPECIAl "I like my meat extra crispy!" +#define SAY_DEATH "What a bloody waste this is!" + +#define SOUND_AGGRO 8899 +#define SOUND_TAUNT1 8903 +#define SOUND_TAUNT2 8904 +#define SOUND_TAUNT3 8905 +#define SOUND_SLAY 8901 +#define SOUND_SPECIAL 8902 +#define SOUND_DEATH 8900 + +#define SPIRIT_OF_KORTHAZZ 16778 + +struct MANGOS_DLL_DECL boss_thane_korthazzAI : public ScriptedAI +{ + boss_thane_korthazzAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Mark_Timer; + uint32 Meteor_Timer; + bool ShieldWall1; + bool ShieldWall2; + + void Reset() + { + Mark_Timer = 20000; // First Horsemen Mark is applied at 20 sec. + Meteor_Timer = 30000; // wrong + ShieldWall1 = true; + ShieldWall2 = true; + } + + void InitialYell() + { + if(!InCombat) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + } + + void KilledUnit() + { + DoYell(SAY_SLAY,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void Aggro(Unit *who) + { + InitialYell(); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + // Mark of Korthazz + if(Mark_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MARK_OF_KORTHAZZ); + Mark_Timer = 12000; + }else Mark_Timer -= diff; + + // Shield Wall - All 4 horsemen will shield wall at 50% hp and 20% hp for 20 seconds + if(ShieldWall1 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 50) + { + if(ShieldWall1) + { + DoCast(m_creature,SPELL_SHIELDWALL); + ShieldWall1 = false; + } + } + if(ShieldWall2 && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20) + { + if(ShieldWall2) + { + DoCast(m_creature,SPELL_SHIELDWALL); + ShieldWall2 = false; + } + } + + // Meteor + if(Meteor_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_METEOR); + Meteor_Timer = 20000; // wrong + }else Meteor_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_thane_korthazz(Creature *_Creature) +{ + return new boss_thane_korthazzAI (_Creature); +} + +void AddSC_boss_thane_korthazz() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_thane_korthazz"; + newscript->GetAI = GetAI_boss_thane_korthazz; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/naxxramas/instance_naxxramas.cpp b/src/bindings/scripts/scripts/zone/naxxramas/instance_naxxramas.cpp new file mode 100644 index 00000000000..655d1e07a88 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/naxxramas/instance_naxxramas.cpp @@ -0,0 +1,24 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Naxxramas +SD%Complete: 0 +SDComment: Place holder +SDCategory: Naxxramas +EndScriptData */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/scripts/zone/netherstorm/netherstorm.cpp b/src/bindings/scripts/scripts/zone/netherstorm/netherstorm.cpp new file mode 100644 index 00000000000..4b41574908f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/netherstorm/netherstorm.cpp @@ -0,0 +1,423 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Netherstorm +SD%Complete: 75 +SDComment: Quest support: 10438, 10652 (special flight paths), 10299,10321,10322,10323,10329,10330,10338,10365(Shutting Down Manaforge) +SDCategory: Netherstorm +EndScriptData */ + +/* ContentData +npc_manaforge_control_console +go_manaforge_control_console +npc_protectorate_nether_drake +npc_veronia +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_manaforge_control_console +######*/ + +#define EMOTE_START "Warning! Emergency shutdown process initiated by $N. Shutdown will complete in two minutes." +#define EMOTE_60 "Emergency shutdown will complete in one minute." +#define EMOTE_30 "Emergency shutdown will complete in thirty seconds." +#define EMOTE_10 "Emergency shutdown will complete in ten seconds." +#define EMOTE_COMPLETE "Emergency shutdown complete." +#define EMOTE_ABORT "Emergency shutdown aborted." + +#define ENTRY_BNAAR_C_CONSOLE 20209 +#define ENTRY_CORUU_C_CONSOLE 20417 +#define ENTRY_DURO_C_CONSOLE 20418 +#define ENTRY_ARA_C_CONSOLE 20440 + +#define ENTRY_SUNFURY_TECH 20218 +#define ENTRY_SUNFURY_PROT 20436 + +#define ENTRY_ARA_TECH 20438 +#define ENTRY_ARA_ENGI 20439 +#define ENTRY_ARA_GORKLONN 20460 + +#define SPELL_DISABLE_VISUAL 35031 +#define SPELL_INTERRUPT_1 35016 //ACID mobs should cast this +#define SPELL_INTERRUPT_2 35176 //ACID mobs should cast this (Manaforge Ara-version) + +struct MANGOS_DLL_DECL npc_manaforge_control_consoleAI : public ScriptedAI +{ + npc_manaforge_control_consoleAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Event_Timer; + uint32 Wave_Timer; + uint32 Phase; + bool Wave; + uint64 someplayer; + uint64 goConsole; + Creature* add; + + void Reset() + { + Event_Timer = 3000; + Wave_Timer = 0; + Phase = 1; + Wave = false; + someplayer = 0; + goConsole = 0; + Creature* add = NULL; + } + + void Aggro(Unit *who) { return; } + + /*void SpellHit(Unit *caster, const SpellEntry *spell) + { + //we have no way of telling the creature was hit by spell -> got aura applied after 10-12 seconds + //then no way for the mobs to actually stop the shutdown as intended. + if( spell->Id == SPELL_INTERRUPT_1 ) + DoSay("Silence! I kill you!",LANG_UNIVERSAL, NULL); + }*/ + + void JustDied(Unit* killer) + { + DoTextEmote(EMOTE_ABORT, NULL); + + if( someplayer ) + { + Unit* p = Unit::GetUnit((*m_creature),someplayer); + if( p && p->GetTypeId() == TYPEID_PLAYER ) + { + switch( m_creature->GetEntry() ) + { + case ENTRY_BNAAR_C_CONSOLE: + ((Player*)p)->FailQuest(10299); + ((Player*)p)->FailQuest(10329); + break; + case ENTRY_CORUU_C_CONSOLE: + ((Player*)p)->FailQuest(10321); + ((Player*)p)->FailQuest(10330); + break; + case ENTRY_DURO_C_CONSOLE: + ((Player*)p)->FailQuest(10322); + ((Player*)p)->FailQuest(10338); + break; + case ENTRY_ARA_C_CONSOLE: + ((Player*)p)->FailQuest(10323); + ((Player*)p)->FailQuest(10365); + break; + } + } + } + + if( goConsole ) + { + if( GameObject* go = GameObject::GetGameObject((*m_creature),goConsole) ) + go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); + } + } + + void DoWaveSpawnForCreature(Creature *creature) + { + switch( creature->GetEntry() ) + { + case ENTRY_BNAAR_C_CONSOLE: + if( rand()%2 ) + { + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2933.68,4162.55,164.00,1.60,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2927.36,4212.97,164.00); + } + else + { + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2927.36,4212.97,164.00,4.94,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2933.68,4162.55,164.00); + } + Wave_Timer = 30000; + break; + case ENTRY_CORUU_C_CONSOLE: + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2445.21,2765.26,134.49,3.93,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2424.21,2740.15,133.81); + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2429.86,2731.85,134.53,1.31,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2435.37,2766.04,133.81); + Wave_Timer = 20000; + break; + case ENTRY_DURO_C_CONSOLE: + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2986.80,2205.36,165.37,3.74,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2985.15,2197.32,164.79); + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2952.91,2191.20,165.32,0.22,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2060.01,2185.27,164.67); + Wave_Timer = 15000; + break; + case ENTRY_ARA_C_CONSOLE: + if( rand()%2 ) + { + add = m_creature->SummonCreature(ENTRY_ARA_TECH,4035.11,4038.97,194.27,2.57,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,4003.42,4040.19,193.49); + add = m_creature->SummonCreature(ENTRY_ARA_TECH,4033.66,4036.79,194.28,2.57,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,4003.42,4040.19,193.49); + add = m_creature->SummonCreature(ENTRY_ARA_TECH,4037.13,4037.30,194.23,2.57,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,4003.42,4040.19,193.49); + } + else + { + add = m_creature->SummonCreature(ENTRY_ARA_TECH,3099.59,4049.30,194.22,0.05,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,4028.01,4035.17,193.59); + add = m_creature->SummonCreature(ENTRY_ARA_TECH,3999.72,4046.75,194.22,0.05,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,4028.01,4035.17,193.59); + add = m_creature->SummonCreature(ENTRY_ARA_TECH,3996.81,4048.26,194.22,0.05,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,4028.01,4035.17,193.59); + } + Wave_Timer = 15000; + break; + } + } + void DoFinalSpawnForCreature(Creature *creature) + { + switch( creature->GetEntry() ) + { + case ENTRY_BNAAR_C_CONSOLE: + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2946.52,4201.42,163.47,3.54,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2927.49,4192.81,163.00); + break; + case ENTRY_CORUU_C_CONSOLE: + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2453.88,2737.85,133.27,2.59,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2433.96,2751.53,133.85); + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2441.62,2735.32,134.49,1.97,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2433.96,2751.53,133.85); + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2450.73,2754.50,134.49,3.29,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2433.96,2751.53,133.85); + break; + case ENTRY_DURO_C_CONSOLE: + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2956.18,2202.85,165.32,5.45,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2972.27,2193.22,164.48); + add = m_creature->SummonCreature(ENTRY_SUNFURY_TECH,2975.30,2211.50,165.32,4.55,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2972.27,2193.22,164.48); + add = m_creature->SummonCreature(ENTRY_SUNFURY_PROT,2965.02,2217.45,164.16,4.96,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,2972.27,2193.22,164.48); + break; + case ENTRY_ARA_C_CONSOLE: + add = m_creature->SummonCreature(ENTRY_ARA_ENGI,3994.51,4020.46,192.18,0.91,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,4008.35,4035.04,192.70); + add = m_creature->SummonCreature(ENTRY_ARA_GORKLONN,4021.56,4059.35,193.59,4.44,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 120000); + if( add ) add->GetMotionMaster()->MovePoint(0,4016.62,4039.89,193.46); + break; + } + } + + void UpdateAI(const uint32 diff) + { + if( Event_Timer < diff ) + { + switch(Phase) + { + case 1: + if( someplayer ) + { + Unit* u = Unit::GetUnit((*m_creature),someplayer); + if( u && u->GetTypeId() == TYPEID_PLAYER ) DoTextEmote(EMOTE_START, u); + } + Event_Timer = 60000; + Wave = true; + ++Phase; + break; + case 2: + DoTextEmote(EMOTE_60, NULL); + Event_Timer = 30000; + ++Phase; + break; + case 3: + DoTextEmote(EMOTE_30, NULL); + Event_Timer = 20000; + DoFinalSpawnForCreature(m_creature); + ++Phase; + break; + case 4: + DoTextEmote(EMOTE_10, NULL); + Event_Timer = 10000; + Wave = false; + ++Phase; + break; + case 5: + DoTextEmote(EMOTE_COMPLETE, NULL); + if( someplayer ) + { + Unit* u = Unit::GetUnit((*m_creature),someplayer); + if( u && u->GetTypeId() == TYPEID_PLAYER ) + ((Player*)u)->KilledMonster(m_creature->GetEntry(),m_creature->GetGUID()); + DoCast(m_creature,SPELL_DISABLE_VISUAL); + } + if( goConsole ) + { + if( GameObject* go = GameObject::GetGameObject((*m_creature),goConsole) ) + go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); + } + ++Phase; + break; + default: + break; + } + } else Event_Timer -= diff; + + if( Wave ) + { + if( Wave_Timer < diff ) + { + DoWaveSpawnForCreature(m_creature); + } else Wave_Timer -= diff; + } + } +}; +CreatureAI* GetAI_npc_manaforge_control_console(Creature *_Creature) +{ + return new npc_manaforge_control_consoleAI (_Creature); +} + +/*###### +## go_manaforge_control_console +######*/ + +//TODO: clean up this workaround when mangos adds support to do it properly (with gossip selections instead of instant summon) +bool GOHello_go_manaforge_control_console(Player *player, GameObject* _GO) +{ + if (_GO->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER) + { + player->PrepareQuestMenu(_GO->GetGUID()); + player->SendPreparedQuest(_GO->GetGUID()); + } + + Creature* manaforge; + manaforge = NULL; + + switch( _GO->GetAreaId() ) + { + case 3726: //b'naar + if( (player->GetQuestStatus(10299) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(10329) == QUEST_STATUS_INCOMPLETE) && + player->HasItemCount(29366,1)) + manaforge = player->SummonCreature(ENTRY_BNAAR_C_CONSOLE,2918.95,4189.98,161.88,0.34,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,125000); + break; + case 3730: //coruu + if( (player->GetQuestStatus(10321) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(10330) == QUEST_STATUS_INCOMPLETE) && + player->HasItemCount(29396,1)) + manaforge = player->SummonCreature(ENTRY_CORUU_C_CONSOLE,2426.77,2750.38,133.24,2.14,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,125000); + break; + case 3734: //duro + if( (player->GetQuestStatus(10322) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(10338) == QUEST_STATUS_INCOMPLETE) && + player->HasItemCount(29397,1)) + manaforge = player->SummonCreature(ENTRY_DURO_C_CONSOLE,2976.48,2183.29,163.20,1.85,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,125000); + break; + case 3722: //ara + if( (player->GetQuestStatus(10323) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(10365) == QUEST_STATUS_INCOMPLETE) && + player->HasItemCount(29411,1)) + manaforge = player->SummonCreature(ENTRY_ARA_C_CONSOLE,4013.71,4028.76,192.10,1.25,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,125000); + break; + } + + if( manaforge ) + { + ((npc_manaforge_control_consoleAI*)manaforge->AI())->someplayer = player->GetGUID(); + ((npc_manaforge_control_consoleAI*)manaforge->AI())->goConsole = _GO->GetGUID(); + _GO->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); + } + return true; +} + +/*###### +## npc_protectorate_nether_drake +######*/ + +bool GossipHello_npc_protectorate_nether_drake(Player *player, Creature *_Creature) +{ + //On Nethery Wings + if (player->GetQuestStatus(10438) == QUEST_STATUS_INCOMPLETE && player->HasItemCount(29778,1) ) + player->ADD_GOSSIP_ITEM(0, "Fly me to Ultris", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_protectorate_nether_drake(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + player->CLOSE_GOSSIP_MENU(); + + std::vector nodes; + + nodes.resize(2); + nodes[0] = 152; //from drake + nodes[1] = 153; //end at drake + player->ActivateTaxiPathTo(nodes); //TaxiPath 627 (possibly 627+628(152->153->154->155) ) + } + return true; +} + +/*###### +## npc_veronia +######*/ + +bool GossipHello_npc_veronia(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + //Behind Enemy Lines + if (player->GetQuestStatus(10652) && !player->GetQuestRewardStatus(10652)) + player->ADD_GOSSIP_ITEM(0, "Fly me to Manaforge Coruu please", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_veronia(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,34905,true); //TaxiPath 606 + } + return true; +} + +/*###### +## +######*/ + +void AddSC_netherstorm() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="go_manaforge_control_console"; + newscript->pGOHello = &GOHello_go_manaforge_control_console; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_manaforge_control_console"; + newscript->GetAI = GetAI_npc_manaforge_control_console; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_protectorate_nether_drake"; + newscript->pGossipHello = &GossipHello_npc_protectorate_nether_drake; + newscript->pGossipSelect = &GossipSelect_npc_protectorate_nether_drake; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_veronia"; + newscript->pGossipHello = &GossipHello_npc_veronia; + newscript->pGossipSelect = &GossipSelect_npc_veronia; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/onyxias_lair/boss_onyxia.cpp b/src/bindings/scripts/scripts/zone/onyxias_lair/boss_onyxia.cpp new file mode 100644 index 00000000000..87ee5b8b0cc --- /dev/null +++ b/src/bindings/scripts/scripts/zone/onyxias_lair/boss_onyxia.cpp @@ -0,0 +1,229 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Onyxia +SD%Complete: 50 +SDComment: Phase 2 has many errors. Recommend Rewrite of entire script. +SDCategory: Onyxia's Lair +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_WINGBUFFET 18500 +#define SPELL_FLAMEBREATH 18435 +#define SPELL_CLEAVE 19983 +#define SPELL_TAILSWEEP 15847 +#define SPELL_KNOCK_AWAY 19633 + +#define SPELL_ENGULFINGFLAMES 20019 +#define SPELL_DEEPBREATH 23461 +#define SPELL_FIREBALL 18392 + +#define SPELL_BELLOWINGROAR 18431 +#define SPELL_HEATED_GROUND 22191 + +#define SPELL_SUMMONWHELP 17646 + +#define SAY_AGGRO "How fortuitous. Usually, I must leave my lair to feed." +#define SAY_KILL "Learn your place mortal!" +#define SAY_PHASE_2_TRANS "This meaningless exertion bores me. I'll incinerate you from above!" +#define SAY_PHASE_3_TRANS "It seems you'll need another lesson!" +#define EMOTE_BREATH "takes a deep breath..." + +static float MovementLocations[7][3]= +{ + {-65.8444, -213.809, -60.2985}, + {22.87639, -217.152, -60.0548}, + {-33.5561, -182.682, -60.9457}, + {-31.4963, -250.123, -60.1278}, + {-2.78999, -181.431, -60.8962}, + {-54.9415, -232.242, -60.5555}, + {10.56655, -241.478, -60.9426}, +}; + +static float SpawnLocations[4][3]= +{ + {-30.127, -254.463, -89.440}, + {-30.817, -177.106, -89.258}, + {14.480, -241.560, -85.6300}, + {17.372, -190.840, -85.2810}, +}; + +#define CREATURE_WHELP 11262 + +struct MANGOS_DLL_DECL boss_onyxiaAI : public ScriptedAI +{ + boss_onyxiaAI(Creature* c) : ScriptedAI(c) {Reset();} + + uint32 Phase; + + uint32 FlameBreathTimer; + uint32 CleaveTimer; + uint32 TailSweepTimer; + uint32 MovementTimer; + uint32 EngulfingFlamesTimer; + uint32 SummonWhelpsTimer; + uint32 BellowingRoarTimer; + uint32 WingBuffetTimer; + + void Reset() + { + Phase = 1; + + FlameBreathTimer = 20000; + TailSweepTimer = 2000; + CleaveTimer = 15000; + MovementTimer = 5000; + EngulfingFlamesTimer = 15000; + SummonWhelpsTimer = 45000; + BellowingRoarTimer = 30000; + WingBuffetTimer = 17000; + } + + void Aggro(Unit* who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + } + + void KilledUnit(Unit *victim) + { + DoYell(SAY_KILL, LANG_UNIVERSAL, NULL); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 60) && (Phase == 1)) + { + Phase = 2; + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT + MOVEMENTFLAG_LEVITATING); + m_creature->GetMotionMaster()->Clear(false); + m_creature->GetMotionMaster()->MoveIdle(); + DoYell(SAY_PHASE_2_TRANS, LANG_UNIVERSAL, NULL); + } + + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 40) && (Phase == 2)) + { + Phase = 3; + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT + MOVEMENTFLAG_LEVITATING); + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + DoYell(SAY_PHASE_3_TRANS, LANG_UNIVERSAL, NULL); + } + + if(Phase == 1 || Phase == 3) + { + if(FlameBreathTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FLAMEBREATH); + FlameBreathTimer = 15000; + }else FlameBreathTimer -= diff; + + if(TailSweepTimer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + if(target && !m_creature->HasInArc(M_PI, target)) + DoCast(target, SPELL_TAILSWEEP); + + TailSweepTimer = 10000; + }else TailSweepTimer -= diff; + + if(CleaveTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CLEAVE); + CleaveTimer = 10000; + }else CleaveTimer -= diff; + + if(WingBuffetTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_WINGBUFFET); + WingBuffetTimer = 7000 + ((rand()%8)*1000); + }else WingBuffetTimer -= diff; + + DoMeleeAttackIfReady(); + } + + if(Phase == 2) + { + if(!m_creature->isHover()) + { + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + m_creature->SetHover(true); + } + + if(!m_creature->GetMotionMaster()->empty() && (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE)) + m_creature->GetMotionMaster()->Clear(false); + + if(MovementTimer < diff) + { + uint32 random = rand()%8; + if(random < 7) + m_creature->GetMotionMaster()->MovePoint(0, MovementLocations[random][0], MovementLocations[random][1], MovementLocations[random][2]); + else + { + DoTextEmote(EMOTE_BREATH, NULL); + DoCast(m_creature->getVictim(), SPELL_DEEPBREATH); + } + MovementTimer = 25000; + }else MovementTimer -= diff; + + if(EngulfingFlamesTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_ENGULFINGFLAMES); + EngulfingFlamesTimer = 10000; + }else EngulfingFlamesTimer -= diff; + + if(SummonWhelpsTimer < diff) + { + uint32 max = rand()%20; + for(uint8 i = 0; i < max; ++i) + { + uint8 random = rand()%4; + Creature* Whelp = m_creature->SummonCreature(CREATURE_WHELP, SpawnLocations[random][0], SpawnLocations[random][1], SpawnLocations[random][2], 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000); + if(Whelp) + Whelp->AI()->AttackStart(SelectUnit(SELECT_TARGET_RANDOM, 0)); + } + SummonWhelpsTimer = 45000; + }else SummonWhelpsTimer -= diff; + } + + if(Phase == 3) + { + if(BellowingRoarTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_BELLOWINGROAR); + BellowingRoarTimer = 30000; + }else BellowingRoarTimer -= diff; + } + } +}; + +CreatureAI* GetAI_boss_onyxiaAI(Creature *_Creature) +{ + return new boss_onyxiaAI (_Creature); +} + +void AddSC_boss_onyxia() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_onyxia"; + newscript->GetAI = GetAI_boss_onyxiaAI; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/orgrimmar/orgrimmar.cpp b/src/bindings/scripts/scripts/zone/orgrimmar/orgrimmar.cpp new file mode 100644 index 00000000000..9840f7711dc --- /dev/null +++ b/src/bindings/scripts/scripts/zone/orgrimmar/orgrimmar.cpp @@ -0,0 +1,265 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Orgrimmar +SD%Complete: 100 +SDComment: Quest support: 2460, 5727, 6566 +SDCategory: Orgrimmar +EndScriptData */ + +/* ContentData +npc_neeru_fireblade npc_text + gossip options text missing +npc_shenthul +npc_thrall_warchief +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_neeru_fireblade +######*/ + +#define QUEST_5727 5727 + +bool GossipHello_npc_neeru_fireblade(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(QUEST_5727) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM(0, "You may speak frankly, Neeru...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(4513, _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_neeru_fireblade(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM(0, "[PH] ...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(4513, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(QUEST_5727); + break; + } + return true; +} + +/*###### +## npc_shenthul +######*/ + +#define QUEST_2460 2460 + +struct MANGOS_DLL_DECL npc_shenthulAI : public ScriptedAI +{ + npc_shenthulAI(Creature* c) : ScriptedAI(c) { Reset(); } + + bool CanTalk; + bool CanEmote; + uint32 Salute_Timer; + uint32 Reset_Timer; + uint64 playerGUID; + + void Reset() + { + CanTalk = false; + CanEmote = false; + Salute_Timer = 6000; + Reset_Timer = 0; + playerGUID = 0; + } + + void Aggro(Unit* who) { } + + void UpdateAI(const uint32 diff) + { + if( CanEmote ) + if( Reset_Timer < diff ) + { + if( Unit* temp = Unit::GetUnit((*m_creature),playerGUID) ) + if( temp->GetTypeId() == TYPEID_PLAYER ) + ((Player*)temp)->FailQuest(QUEST_2460); + Reset(); + } else Reset_Timer -= diff; + + if( CanTalk && !CanEmote ) + if( Salute_Timer < diff ) + { + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + CanEmote = true; + Reset_Timer = 60000; + } else Salute_Timer -= diff; + + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_shenthul(Creature *_Creature) +{ + return new npc_shenthulAI (_Creature); +} + +bool QuestAccept_npc_shenthul(Player* player, Creature* creature, Quest const* quest) +{ + if( quest->GetQuestId() == QUEST_2460 ) + { + ((npc_shenthulAI*)creature->AI())->CanTalk = true; + ((npc_shenthulAI*)creature->AI())->playerGUID = player->GetGUID(); + } + return true; +} + +bool ReciveEmote_npc_shenthul(Player *player, Creature *_Creature, uint32 emote) +{ + if( emote == TEXTEMOTE_SALUTE && player->GetQuestStatus(QUEST_2460) == QUEST_STATUS_INCOMPLETE ) + if( ((npc_shenthulAI*)_Creature->AI())->CanEmote ) + { + player->AreaExploredOrEventHappens(QUEST_2460); + ((npc_shenthulAI*)_Creature->AI())->Reset(); + } + return true; +} + +/*###### +## npc_thrall_warchief +######*/ + +#define QUEST_6566 6566 + +#define SPELL_CHAIN_LIGHTNING 16033 +#define SPELL_SHOCK 16034 + +//TODO: verify abilities/timers +struct MANGOS_DLL_DECL npc_thrall_warchiefAI : public ScriptedAI +{ + npc_thrall_warchiefAI(Creature* c) : ScriptedAI(c) { Reset(); } + + uint32 ChainLightning_Timer; + uint32 Shock_Timer; + + void Reset() + { + ChainLightning_Timer = 2000; + Shock_Timer = 8000; + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( ChainLightning_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_CHAIN_LIGHTNING); + ChainLightning_Timer = 9000; + }else ChainLightning_Timer -= diff; + + if( Shock_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_SHOCK); + Shock_Timer = 15000; + }else Shock_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_thrall_warchief(Creature *_Creature) +{ + return new npc_thrall_warchiefAI (_Creature); +} + +bool GossipHello_npc_thrall_warchief(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(QUEST_6566) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM(0, "Please share your wisdom with me, Warchief.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_thrall_warchief(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM(0, "What discoveries?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(5733, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM(0, "Usurper?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->SEND_GOSSIP_MENU(5734, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM(0, "With all due respect, Warchief - why not allow them to be destroyed? Does this not strengthen our position?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + player->SEND_GOSSIP_MENU(5735, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM(0, "I... I did not think of it that way, Warchief.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(5736, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->ADD_GOSSIP_ITEM(0, "I live only to serve, Warchief! My life is empty and meaningless without your guidance.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+6); + player->SEND_GOSSIP_MENU(5737, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + player->ADD_GOSSIP_ITEM(0, "Of course, Warchief!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+7); + player->SEND_GOSSIP_MENU(5738, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+7: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(QUEST_6566); + break; + } + return true; +} + +void AddSC_orgrimmar() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_neeru_fireblade"; + newscript->pGossipHello = &GossipHello_npc_neeru_fireblade; + newscript->pGossipSelect = &GossipSelect_npc_neeru_fireblade; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_shenthul"; + newscript->GetAI = GetAI_npc_shenthul; + newscript->pQuestAccept = &QuestAccept_npc_shenthul; + newscript->pReceiveEmote = &ReciveEmote_npc_shenthul; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_thrall_warchief"; + newscript->GetAI = GetAI_npc_thrall_warchief; + newscript->pGossipHello = &GossipHello_npc_thrall_warchief; + newscript->pGossipSelect = &GossipSelect_npc_thrall_warchief; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/razorfen_downs/boss_amnennar_the_coldbringer.cpp b/src/bindings/scripts/scripts/zone/razorfen_downs/boss_amnennar_the_coldbringer.cpp new file mode 100644 index 00000000000..d25781c0ca6 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/razorfen_downs/boss_amnennar_the_coldbringer.cpp @@ -0,0 +1,140 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Amnennar_the_coldbringer +SD%Complete: 100 +SDComment: +SDCategory: Razorfen Downs +EndScriptData */ + +#include "precompiled.h" + +#define SAY_0 "You'll never leave this place... alive." +#define SAY_1 "Come, spirits, attend your master." +#define SAY_SLAY "Too...easy!" +#define SOUND_AGGRO 5825 +#define SOUND_SLAY 5826 +#define SOUND_SUMMON 5829 + +#define SPELL_AMNENNARSWRATH 13009 +#define SPELL_FROSTBOLT 10179 + +struct MANGOS_DLL_DECL boss_amnennar_the_coldbringerAI : public ScriptedAI +{ + boss_amnennar_the_coldbringerAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 AmnenarsWrath_Timer; + uint32 FrostBolt_Timer; + bool Spectrals; + int Rand; + int RandX; + int RandY; + Creature* Summoned; + + void Reset() + { + AmnenarsWrath_Timer = 8000; + FrostBolt_Timer = 1000; + Spectrals = false; + } + + void Aggro(Unit *who) + { + DoYell(SAY_0,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void KilledUnit() + { + DoYell(SAY_SLAY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY); + } + + void SummonSpectrals(Unit* victim) + { + Rand = rand()%5; + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = rand()%5; + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + Summoned = DoSpawnCreature(8585, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); + if(Summoned) + ((CreatureAI*)Summoned->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //AmnenarsWrath_Timer + if (AmnenarsWrath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_AMNENNARSWRATH); + AmnenarsWrath_Timer = 12000; + } else AmnenarsWrath_Timer -= diff; + + //FrostBolt_Timer + if (FrostBolt_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_FROSTBOLT); + + FrostBolt_Timer = 8000; + } else FrostBolt_Timer -= diff; + + if ( !Spectrals && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 50 ) + { + DoYell(SAY_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SUMMON); + + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + SummonSpectrals(target); + SummonSpectrals(target); + SummonSpectrals(target); + Spectrals = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_amnennar_the_coldbringer(Creature *_Creature) +{ + return new boss_amnennar_the_coldbringerAI (_Creature); +} + +void AddSC_boss_amnennar_the_coldbringer() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_amnennar_the_coldbringer"; + newscript->GetAI = GetAI_boss_amnennar_the_coldbringer; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_ayamiss.cpp b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_ayamiss.cpp new file mode 100644 index 00000000000..f448351d255 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_ayamiss.cpp @@ -0,0 +1,107 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Ayamiss +SD%Complete: 50 +SDComment: VERIFY SCRIPT +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +/* +To do: +make him fly from 70-100% +*/ + +#define SPELL_STINGERSPRAY 25749 +#define SPELL_POISONSTINGER 25748 //only used in phase1 +#define SPELL_SUMMONSWARMER 25844 //might be 25708 +// #define SPELL_PARALYZE 23414 doesnt work correct (core) + +struct MANGOS_DLL_DECL boss_ayamissAI : public ScriptedAI +{ + boss_ayamissAI(Creature *c) : ScriptedAI(c) {Reset();} + + Unit *pTarget; + uint32 STINGERSPRAY_Timer; + uint32 POISONSTINGER_Timer; + uint32 SUMMONSWARMER_Timer; + uint32 phase; + + void Reset() + { + pTarget = NULL; + STINGERSPRAY_Timer = 30000; + POISONSTINGER_Timer = 30000; + SUMMONSWARMER_Timer = 60000; + phase=1; + } + + void Aggro(Unit *who) + { + pTarget = who; + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //If he is 70% start phase 2 + if (phase==1 && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 70 && !m_creature->IsNonMeleeSpellCasted(false)) + { + phase=2; + } + + //STINGERSPRAY_Timer (only in phase2) + if (phase==2 && STINGERSPRAY_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_STINGERSPRAY); + STINGERSPRAY_Timer = 30000; + }else STINGERSPRAY_Timer -= diff; + + //POISONSTINGER_Timer (only in phase1) + if (phase==1 && POISONSTINGER_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_POISONSTINGER); + POISONSTINGER_Timer = 30000; + }else POISONSTINGER_Timer -= diff; + + //SUMMONSWARMER_Timer (only in phase1) + if (SUMMONSWARMER_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SUMMONSWARMER); + SUMMONSWARMER_Timer = 60000; + }else SUMMONSWARMER_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_ayamiss(Creature *_Creature) +{ + return new boss_ayamissAI (_Creature); +} + +void AddSC_boss_ayamiss() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_ayamiss"; + newscript->GetAI = GetAI_boss_ayamiss; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_buru.cpp b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_buru.cpp new file mode 100644 index 00000000000..65bec030fe9 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_buru.cpp @@ -0,0 +1,24 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Buru +SD%Complete: 0 +SDComment: Place Holder +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_kurinnaxx.cpp b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_kurinnaxx.cpp new file mode 100644 index 00000000000..8b7411dbaa3 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_kurinnaxx.cpp @@ -0,0 +1,93 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Kurinnaxx +SD%Complete: 100 +SDComment: VERIFY SCRIPT AND SQL +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_MORTALWOUND 25646 +#define SPELL_SANDTRAP 25656 +#define SPELL_ENRAGE 28798 + +struct MANGOS_DLL_DECL boss_kurinnaxxAI : public ScriptedAI +{ + boss_kurinnaxxAI(Creature *c) : ScriptedAI(c) {Reset();} + + Unit *pTarget; + uint32 MORTALWOUND_Timer; + uint32 SANDTRAP_Timer; + uint32 i; + + void Reset() + { + i=0; + pTarget = NULL; + MORTALWOUND_Timer = 30000; + SANDTRAP_Timer = 30000; + } + + void Aggro(Unit *who) + { + pTarget = who; + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are <30% cast enrage + if (i==0 && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 30 && !m_creature->IsNonMeleeSpellCasted(false)) + { + i=1; + DoCast(m_creature->getVictim(),SPELL_ENRAGE); + } + + //MORTALWOUND_Timer + if (MORTALWOUND_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MORTALWOUND); + MORTALWOUND_Timer = 30000; + }else MORTALWOUND_Timer -= diff; + + //SANDTRAP_Timer + if (SANDTRAP_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SANDTRAP); + SANDTRAP_Timer = 30000; + }else SANDTRAP_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_kurinnaxx(Creature *_Creature) +{ + return new boss_kurinnaxxAI (_Creature); +} + +void AddSC_boss_kurinnaxx() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_kurinnaxx"; + newscript->GetAI = GetAI_boss_kurinnaxx; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_moam.cpp b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_moam.cpp new file mode 100644 index 00000000000..42d30bb4eca --- /dev/null +++ b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_moam.cpp @@ -0,0 +1,117 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Moam +SD%Complete: 100 +SDComment: VERIFY SCRIPT AND SQL +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_TRAMPLE 15550 +#define SPELL_DRAINMANA 27256 +#define SPELL_ARCANEERUPTION 25672 +#define SPELL_SUMMONMANA 25681 +#define SPELL_GRDRSLEEP 24360 //Greater Dreamless Sleep + +#define SAY_MANA "moam bristles with energy!" + +struct MANGOS_DLL_DECL boss_moamAI : public ScriptedAI +{ + boss_moamAI(Creature *c) : ScriptedAI(c) {Reset();} + + Unit *pTarget; + uint32 TRAMPLE_Timer; + uint32 DRAINMANA_Timer; + uint32 SUMMONMANA_Timer; + uint32 i; + uint32 j; + + void Reset() + { + i=0; + j=0; + pTarget = NULL; + TRAMPLE_Timer = 30000; + DRAINMANA_Timer = 30000; + } + + void Aggro(Unit *who) + { + pTarget = who; + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are 100%MANA cast Arcane Erruption + //if (j==1 && m_creature->GetMana()*100 / m_creature->GetMaxMana() == 100 && !m_creature->IsNonMeleeSpellCasted(false)) + { + DoCast(m_creature->getVictim(),SPELL_ARCANEERUPTION); + DoYell(SAY_MANA,LANG_UNIVERSAL,NULL); + } + + //If we are <50%HP cast MANA FIEND (Summon Mana) and Sleep + //if (i==0 && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 50 && !m_creature->IsNonMeleeSpellCasted(false)) + { + i=1; + DoCast(m_creature->getVictim(),SPELL_SUMMONMANA); + DoCast(m_creature->getVictim(),SPELL_GRDRSLEEP); + } + + //SUMMONMANA_Timer + if (i==1 && SUMMONMANA_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SUMMONMANA); + SUMMONMANA_Timer = 90000; + }else SUMMONMANA_Timer -= diff; + + //TRAMPLE_Timer + if (TRAMPLE_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_TRAMPLE); + j=1; + + TRAMPLE_Timer = 30000; + }else TRAMPLE_Timer -= diff; + + //DRAINMANA_Timer + if (DRAINMANA_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_DRAINMANA); + DRAINMANA_Timer = 30000; + }else DRAINMANA_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_moam(Creature *_Creature) +{ + return new boss_moamAI (_Creature); +} + +void AddSC_boss_moam() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_moam"; + newscript->GetAI = GetAI_boss_moam; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_ossirian.cpp b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_ossirian.cpp new file mode 100644 index 00000000000..a2a04673925 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_ossirian.cpp @@ -0,0 +1,36 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Ossirian +SD%Complete: 0 +SDComment: Place holder +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +//Ossirian +//8593 I am rejuvinated! +//8594 I have... failed +//8595 My powers are renewed! +//8596 My powers return! +//8597 Protect the city at all costs! +//8598 Sands of the desert rise and block out the sun! +//8599 The walls have been breached! +//8600 To your posts. Defend the city. +//8601 Tresspassers will be terminated. +//8602 You are terminated. diff --git a/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_rajaxx.cpp b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_rajaxx.cpp new file mode 100644 index 00000000000..3daff71f58a --- /dev/null +++ b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/boss_rajaxx.cpp @@ -0,0 +1,42 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Rajaxx +SD%Complete: 0 +SDComment: Place Holder +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +//Rajaxx +//8603 Attack and make them pay dearly! +//8604 Breath your last! +//8605 Crush them! Drive them out! +//8606 Do not hesitate! Destroy them! +//8613 Warriors! Captians! Continue the fight... +//8614 You are not worth my time $N! +//8612 The time of our retribution is at hand! Let darkness reign in the hearts of our enemies! +//8610 No longer will we wait behind barred doors and walls of stone! No longer will our vengeance be denied! The dragons themselves will tremble before our wrath! +//8608 Fear is for the enemy! Fear and death! +//8611 Staghelm will whimper and beg for his life, just as his whelp of a son did! One thousand years of injustice will end this day! +//8607 Fandral! Your time has come! Go and hide in the Emerald Dream and pray we never find you! +//8609 Impudent fool! I will kill you myself! + +//Andorov - no sound +//"Remember, Rajaxx, when I said I'd kill you last? I lied..." +//"They come now. Try not to get yourself killed, young blood." diff --git a/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp new file mode 100644 index 00000000000..a2bddd52efe --- /dev/null +++ b/src/bindings/scripts/scripts/zone/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp @@ -0,0 +1,24 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Ruins_of_Ahnqiraj +SD%Complete: 0 +SDComment: Place holder +SDCategory: Ruins of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_arcanist_doan.cpp b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_arcanist_doan.cpp new file mode 100644 index 00000000000..3a6a3526269 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_arcanist_doan.cpp @@ -0,0 +1,171 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Arcanist_Doan +SD%Complete: 100 +SDComment: +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_POLYMORPH 12826 +#define SPELL_AOESILENCE 8988 +#define SPELL_ARCANEEXPLOSION3 8438 +#define SPELL_ARCANEEXPLOSION4 8439 +#define SPELL_FIREAOE 9435 +#define SPELL_BLINK 1953 +#define SPELL_FIREBALL 21162 +#define SPELL_MANASHIELD4 10191 +#define SPELL_ARCANEBUBBLE 9438 + +#define SAY_AGGRO "You will not defile these mysteries!" +#define SAY_SPECIALAE "Burn in righteous fire!" + +#define SOUND_AGGRO 5842 +#define SOUND_SPECIALAE 5843 + +struct MANGOS_DLL_DECL boss_arcanist_doanAI : public ScriptedAI +{ + boss_arcanist_doanAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FullAOE_Timer; + uint32 Polymorph_Timer; + uint32 Yell_Timer; + uint32 ArcaneBubble_Timer; + uint32 AoESilence_Timer; + uint32 ArcaneExplosion3_Timer; + uint32 ArcaneExplosion4_Timer; + uint32 Blink_Timer; + uint32 Fireball_Timer; + uint32 ManaShield4_Timer; + + void Reset() + { + FullAOE_Timer = 5000; + Polymorph_Timer = 1; + Yell_Timer = 2000; + ArcaneBubble_Timer = 3000; + AoESilence_Timer = 20000; + ArcaneExplosion3_Timer = 10000; + ArcaneExplosion4_Timer = 10000; + Blink_Timer = 40000; + Fireball_Timer = 6000; + ManaShield4_Timer = 70000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are <50% hp cast Arcane Bubble and start casting SPECIAL FIRE AOE + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 50 && !m_creature->IsNonMeleeSpellCasted(false)) + { + if (Polymorph_Timer < diff) + { + Unit* target = NULL; + + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target)DoCast(target,SPELL_POLYMORPH); + Polymorph_Timer = 40000; + }else Polymorph_Timer -= diff; + + if (Yell_Timer < diff) + { + DoYell(SAY_SPECIALAE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SPECIALAE); + Yell_Timer = 40000; + }else Yell_Timer -= diff; + + if (ArcaneBubble_Timer < diff) + { + DoCast(m_creature,SPELL_ARCANEBUBBLE); + ArcaneBubble_Timer = 40000; + }else ArcaneBubble_Timer -= diff; + + if (FullAOE_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIREAOE); + FullAOE_Timer = 40000; + }else FullAOE_Timer -= diff; + } + + //AoESilence_Timer + if (AoESilence_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_AOESILENCE); + AoESilence_Timer = 30000; + }else AoESilence_Timer -= diff; + + //ArcaneExplosion3_Timer + if (ArcaneExplosion3_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ARCANEEXPLOSION3); + ArcaneExplosion3_Timer = 8000; + }else ArcaneExplosion3_Timer -= diff; + + //ArcaneExplosion4_Timer + if (ArcaneExplosion4_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ARCANEEXPLOSION4); + ArcaneExplosion4_Timer = 10000; + }else ArcaneExplosion4_Timer -= diff; + + //Blink_Timer + if (Blink_Timer < diff) + { + DoCast(m_creature,SPELL_BLINK); + Blink_Timer = 30000; + }else Blink_Timer -= diff; + + //Fireball_Timer + if (Fireball_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIREBALL); + Fireball_Timer = 12000; + }else Fireball_Timer -= diff; + + //ManaShiled4_Timer + if (ManaShield4_Timer < diff) + { + DoCast(m_creature,SPELL_MANASHIELD4); + ManaShield4_Timer = 70000; + }else ManaShield4_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_arcanist_doan(Creature *_Creature) +{ + return new boss_arcanist_doanAI (_Creature); +} + +void AddSC_boss_arcanist_doan() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_arcanist_doan"; + newscript->GetAI = GetAI_boss_arcanist_doan; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_azshir_the_sleepless.cpp b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_azshir_the_sleepless.cpp new file mode 100644 index 00000000000..d975cb3d192 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_azshir_the_sleepless.cpp @@ -0,0 +1,97 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Azshir_the_Sleepless +SD%Complete: 80 +SDComment: +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_CALLOFTHEGRAVE 17831 +#define SPELL_TERRIFY 7399 +#define SPELL_SOULSIPHON 7290 + +struct MANGOS_DLL_DECL boss_azshir_the_sleeplessAI : public ScriptedAI +{ + boss_azshir_the_sleeplessAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 SoulSiphon_Timer; + uint32 CallOftheGrave_Timer; + uint32 Terrify_Timer; + + void Reset() + { + SoulSiphon_Timer = 1; + CallOftheGrave_Timer = 30000; + Terrify_Timer = 20000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are <50% hp cast Soul Siphon rank 1 + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 50 && !m_creature->IsNonMeleeSpellCasted(false)) + { + //SoulSiphon_Timer + if (SoulSiphon_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SOULSIPHON); + return; + + SoulSiphon_Timer = 20000; + }else SoulSiphon_Timer -= diff; + } + + //CallOfTheGrave_Timer + if (CallOftheGrave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CALLOFTHEGRAVE); + CallOftheGrave_Timer = 30000; + }else CallOftheGrave_Timer -= diff; + + //Terrify_Timer + if (Terrify_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_TERRIFY); + Terrify_Timer = 20000; + }else Terrify_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_azshir_the_sleepless(Creature *_Creature) +{ + return new boss_azshir_the_sleeplessAI (_Creature); +} + +void AddSC_boss_azshir_the_sleepless() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_azshir_the_sleepless"; + newscript->GetAI = GetAI_boss_azshir_the_sleepless; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_bloodmage_thalnos.cpp b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_bloodmage_thalnos.cpp new file mode 100644 index 00000000000..a130df0fa56 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_bloodmage_thalnos.cpp @@ -0,0 +1,136 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Bloodmage_Thalnos +SD%Complete: 100 +SDComment: +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FROSTNOVA2 865 +#define SPELL_FLAMESHOCK3 8053 +#define SPELL_SHADOWBOLT5 1106 +#define SPELL_FLAMESPIKE 8814 +#define SPELL_FIRENOVA 16079 + +#define SAY_AGGRO "We hunger for vengeance." +#define SAY_HEALTH "No rest... for the angry dead!" +#define SAY_DEATH "More... More souls!" + +#define SOUND_AGGRO 5844 +#define SOUND_HEALTH 5846 +#define SOUND_DEATH 5845 + +struct MANGOS_DLL_DECL boss_bloodmage_thalnosAI : public ScriptedAI +{ + boss_bloodmage_thalnosAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FrostNova2_Timer; + uint32 FlameShock3_Timer; + uint32 ShadowBolt5_Timer; + uint32 FlameSpike_Timer; + uint32 FireNova_Timer; + uint32 Yell_Timer; + + void Reset() + { + Yell_Timer = 1; + FrostNova2_Timer = 10000; + FlameShock3_Timer = 15000; + ShadowBolt5_Timer = 20000; + FlameSpike_Timer = 20000; + FireNova_Timer = 10000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are <35% hp + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 35) + { + Yell_Timer -= diff; + + if (Yell_Timer < diff) + { + DoYell(SAY_HEALTH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_HEALTH); + Yell_Timer = 900000; + } + } + + //FrostNova2_Timer + if (FrostNova2_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROSTNOVA2); + FrostNova2_Timer = 10000; + }else FrostNova2_Timer -= diff; + + //FlameShock3_Timer + if (FlameShock3_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FLAMESHOCK3); + FlameShock3_Timer = 15000; + }else FlameShock3_Timer -= diff; + + //ShadowBolt5_Timer + if (ShadowBolt5_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWBOLT5); + ShadowBolt5_Timer = 20000; + }else ShadowBolt5_Timer -= diff; + + //FlameSpike_Timer + if (FlameSpike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FLAMESPIKE); + FlameSpike_Timer = 30000; + }else FlameSpike_Timer -= diff; + + //FireNova_Timer + if (FireNova_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIRENOVA); + FireNova_Timer = 20000; + }else FireNova_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_bloodmage_thalnos(Creature *_Creature) +{ + return new boss_bloodmage_thalnosAI (_Creature); +} + +void AddSC_boss_bloodmage_thalnos() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_bloodmage_thalnos"; + newscript->GetAI = GetAI_boss_bloodmage_thalnos; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_herod.cpp b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_herod.cpp new file mode 100644 index 00000000000..ea4cdc52e03 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_herod.cpp @@ -0,0 +1,197 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Herod +SD%Complete: 90 +SDComment: Missing adds spawn at death +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_RUSHINGCHARGE 32021 +#define SPELL_RUSHINGCHARGE1 6268 + +#define SPELL_CLEAVE 11608 +#define SPELL_WHIRLWIND 8989 +#define SPELL_SUNDERARMOR 16145 +#define SPELL_REND 21949 +#define SPELL_THUNDERCLAP 15588 +#define SPELL_SLAM 11430 +#define SPELL_BERSERKERSTANCE 2458 +#define SPELL_ENRAGE 28747 +#define SPELL_FIREBALL11 10151 +#define SPELL_CONEOFCOLD5 10161 + +#define SAY_AGGRO "Ah, I have been waiting for a real challenge!" +#define SAY_WHIRLWIND "Blades of Light!" +#define SAY_ENRAGE "Light, give me strength!" +#define SAY_DEATH "Hah, is that all?" + +#define SOUND_AGGRO 5830 +#define SOUND_WHIRLWIND 5832 +#define SOUND_ENRAGE 5833 +#define SOUND_DEATH 5831 + +struct MANGOS_DLL_DECL boss_herodAI : public ScriptedAI +{ + boss_herodAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Yell_Timer; + uint32 Enrage_Timer; + uint32 Cleave_Timer; + uint32 Whirlwind_Timer; + uint32 SunderArmor_Timer; + uint32 Rend_Timer; + uint32 ThunderClap_Timer; + uint32 Slam_Timer; + uint32 Fireball11_Timer; + uint32 ConeOfCold5_Timer; + + void Reset() + { + Yell_Timer = 58000; + Whirlwind_Timer = 60000; + Enrage_Timer = 0; + Cleave_Timer = 15000; + SunderArmor_Timer = 40000; + Rend_Timer = 25000; + ThunderClap_Timer = 25000; + Slam_Timer = 20000; + Fireball11_Timer = 30000; + ConeOfCold5_Timer = 40000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + + //Activate Berserker Stance + DoCast(m_creature,SPELL_BERSERKERSTANCE); + + //Switch between 2 different charge methods + switch (rand()%2) + { + case 0: + DoCast(m_creature,SPELL_RUSHINGCHARGE); + break; + case 1: + DoCast(m_creature,SPELL_RUSHINGCHARGE1); + break; + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are <10% hp goes Enraged + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 10 && !m_creature->IsNonMeleeSpellCasted(false) && Enrage_Timer < diff) + { + DoYell(SAY_ENRAGE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_ENRAGE); + + DoCast(m_creature,SPELL_ENRAGE); + + //Shouldn't cast this agian + Enrage_Timer = diff; + } + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 15000; + }else Cleave_Timer -= diff; + + //Yelling and Whirlwind casting + if (Yell_Timer < diff) + { + //Say Whirlwind monologe + DoYell(SAY_WHIRLWIND,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_WHIRLWIND); + + Yell_Timer = 30000; + }else Yell_Timer -= diff; + + if (Whirlwind_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_WHIRLWIND); + Whirlwind_Timer = 30000; + }else Whirlwind_Timer -= diff; + + //SunderArmor_Timer + if (SunderArmor_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SUNDERARMOR); + SunderArmor_Timer = 40000; + }else SunderArmor_Timer -= diff; + + //Rend_Timer + if (Rend_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_REND); + Rend_Timer = 25000; + }else Rend_Timer -= diff; + + //ThunderClap_Timer + if (ThunderClap_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_THUNDERCLAP); + ThunderClap_Timer = 20000; + }else ThunderClap_Timer -= diff; + + //Slam_Timer + if (Slam_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SLAM); + Slam_Timer = 20000; + }else Slam_Timer -= diff; + + //Fireball11_Timer + if (Fireball11_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FIREBALL11); + Fireball11_Timer = 30000; + }else Fireball11_Timer -= diff; + + //ConeOfCold5_Timer + if (ConeOfCold5_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CONEOFCOLD5); + ConeOfCold5_Timer = 40000; + }else ConeOfCold5_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_herod(Creature *_Creature) +{ + return new boss_herodAI (_Creature); +} + +void AddSC_boss_herod() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_herod"; + newscript->GetAI = GetAI_boss_herod; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp new file mode 100644 index 00000000000..2817b50eb4c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp @@ -0,0 +1,132 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_High_Inquisitor_Faribanks +SD%Complete: 100 +SDComment: +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SLEEP2 1090 +#define SPELL_CURSEOFBLOOD 16098 +#define SPELL_SMITE 6060 +#define SPELL_SHADOWWORDPAIN 2767 +#define SPELL_FLASHHEAL4 9474 +#define SPELL_RENEW6 6078 +#define SPELL_DEVOURINGPLAGUE3 19277 +#define SPELL_MINDBLAST5 8105 + +struct MANGOS_DLL_DECL boss_high_inquisitor_fairbanksAI : public ScriptedAI +{ + boss_high_inquisitor_fairbanksAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Healing_Timer; + uint32 Sleep2_Timer; + uint32 Smite_Timer; + uint32 ShadowWordPain_Timer; + uint32 CurseOfBlood_Timer; + uint32 DevouringPlague3_Timer; + uint32 MindBlast5_Timer; + + void Reset() + { + Healing_Timer = 300; + Sleep2_Timer = 45000; + Smite_Timer = 30000; + ShadowWordPain_Timer = 30000; + CurseOfBlood_Timer = 45000; + DevouringPlague3_Timer = 60000; + MindBlast5_Timer = 20000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are <45% hp cast Renew rank 6 or Flash heal rank 4 + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 45 && !m_creature->IsNonMeleeSpellCasted(false) && Healing_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_RENEW6 || SPELL_FLASHHEAL4); + Healing_Timer = 30000; + }else Healing_Timer -= diff; + + //Sleep2_Timer + if (Sleep2_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SLEEP2); + Sleep2_Timer = 45000; + }else Sleep2_Timer -= diff; + + //Smite_Timer + if (Smite_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SMITE); + Smite_Timer = 20000; + }else Smite_Timer -= diff; + + //ShadowWordPain_Timer + if (ShadowWordPain_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWWORDPAIN); + ShadowWordPain_Timer = 30000; + }else ShadowWordPain_Timer -= diff; + + //CurseOfBlood_Timer + if (CurseOfBlood_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CURSEOFBLOOD); + CurseOfBlood_Timer = 25000; + }else CurseOfBlood_Timer -= diff; + + //DevouringPlague3_Timer + if (DevouringPlague3_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_DEVOURINGPLAGUE3); + DevouringPlague3_Timer = 35000; + }else DevouringPlague3_Timer -= diff; + + //MindBlast5_Timer + if (MindBlast5_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MINDBLAST5); + MindBlast5_Timer = 30000; + }else MindBlast5_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_high_inquisitor_fairbanks(Creature *_Creature) +{ + return new boss_high_inquisitor_fairbanksAI (_Creature); +} + +void AddSC_boss_high_inquisitor_fairbanks() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_high_inquisitor_fairbanks"; + newscript->GetAI = GetAI_boss_high_inquisitor_fairbanks; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_high_inquisitor_whitemane.cpp b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_high_inquisitor_whitemane.cpp new file mode 100644 index 00000000000..3a7c03a03b4 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_high_inquisitor_whitemane.cpp @@ -0,0 +1,177 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_High_Inquistor_Whitmane +SD%Complete: 50 +SDComment: Missing connection with commander mograine +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_DEEPSLEEP 9256 +#define SPELL_SCARLETRESURRECTION 9232 + +#define SPELL_CRUSADERSTRIKE 17281 +#define SPELL_HAMMEROFJUSTICE 13005 +#define SPELL_HOLYSMITE6 9481 +#define SPELL_HOLYFIRE5 15265 +#define SPELL_MINDBLAST6 8106 + +#define SPELL_POWERWORDSHIELD 6065 + +#define SPELL_RENEW 6078 +#define SPELL_FLASHHEAL6 10916 + +#define SAY_AGGRO "There is no escape for you. The Crusade shall destroy all who carry the Scourge's taint." +#define SAY_SPAWN "What, Mograine has fallen? You shall pay for this treachery! " +#define SAY_RES "Arise, my champion!" +#define SAY_DEATH "The Light has spoken!" + +//#define SOUND_AGGRO +#define SOUND_RES 5840 +#define SOUND_SPAWN 5838 +#define SOUND_DEATH 5839 + +struct MANGOS_DLL_DECL boss_high_inquisitor_whitemaneAI : public ScriptedAI +{ + boss_high_inquisitor_whitemaneAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Healing_Timer; + uint32 Renew_Timer; + uint32 PowerWordShield_Timer; + uint32 CrusaderStrike_Timer; + uint32 HammerOfJustice_Timer; + uint32 HolySmite6_Timer; + uint32 HolyFire5_Timer; + uint32 MindBlast6_Timer; + + void Reset() + { + Healing_Timer = 0; + Renew_Timer= 0; + PowerWordShield_Timer = 2000; + CrusaderStrike_Timer = 12000; + HammerOfJustice_Timer = 18000; + HolySmite6_Timer = 10000; + HolyFire5_Timer = 20000; + MindBlast6_Timer = 6000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + /* + //This is going to be a routine to make the resurrection event... + if (m_creature->isAlive && m_creature->isAlive) + { + m_creature->Relocate(1163.113370,1398.856812,32.527786,3.171014); + + DoYell(SAY_SPAWN,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SPAWN); + DoCast(m_creature->getVictim(),SPELL_DEEPSLEEP); + DoCast(m-creature->GetGUID(51117),SPELL_SCARLETRESURRECTION) + } + */ + + //If we are <75% hp cast healing spells at self and Mograine + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 75 ) + { + if (Healing_Timer < diff) + { + DoCast(m_creature,SPELL_FLASHHEAL6); + return; + + //22-32 seconds until we should cast this agian + Healing_Timer = 22000 + rand()%10000; + }else Healing_Timer -= diff; + } + + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 30) + { + if (Renew_Timer < diff) + { + DoCast(m_creature,SPELL_RENEW); + Renew_Timer = 30000; + }else Renew_Timer -= diff; + } + + //PowerWordShield_Timer + if (PowerWordShield_Timer < diff) + { + DoCast(m_creature,SPELL_POWERWORDSHIELD); + PowerWordShield_Timer = 25000; + }else PowerWordShield_Timer -= diff; + + //CrusaderStrike_Timer + if (CrusaderStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CRUSADERSTRIKE); + CrusaderStrike_Timer = 15000; + }else CrusaderStrike_Timer -= diff; + + //HammerOfJustice_Timer + if (HammerOfJustice_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HAMMEROFJUSTICE); + HammerOfJustice_Timer = 12000; + }else HammerOfJustice_Timer -= diff; + + //HolySmite6_Timer + if (HolySmite6_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HOLYSMITE6); + HolySmite6_Timer = 10000; + }else HolySmite6_Timer -= diff; + + //HolyFire5_Timer + if (HolyFire5_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HOLYFIRE5); + HolyFire5_Timer = 15000; + }else HolyFire5_Timer -= diff; + + //MindBlast6_Timer + if (MindBlast6_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MINDBLAST6); + MindBlast6_Timer = 8000; + }else MindBlast6_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_high_inquisitor_whitemane(Creature *_Creature) +{ + return new boss_high_inquisitor_whitemaneAI (_Creature); +} + +void AddSC_boss_high_inquisitor_whitemane() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_high_inquisitor_whitemane"; + newscript->GetAI = GetAI_boss_high_inquisitor_whitemane; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_houndmaster_loksey.cpp b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_houndmaster_loksey.cpp new file mode 100644 index 00000000000..342424eed2f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_houndmaster_loksey.cpp @@ -0,0 +1,78 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Houndmaster_Loksey +SD%Complete: 100 +SDComment: +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SUMMONSCARLETHOUND 17164 +#define SPELL_ENRAGE 28747 + +#define SAY_AGGRO "Release the hounds!" +#define SOUND_AGGRO 5841 + +struct MANGOS_DLL_DECL boss_houndmaster_lokseyAI : public ScriptedAI +{ + boss_houndmaster_lokseyAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Enrage_Timer; + + void Reset() + { + Enrage_Timer = 6000000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + + DoCast(m_creature,SPELL_SUMMONSCARLETHOUND); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are <10% hp cast healing spells at self and Mograine + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 10 && !m_creature->IsNonMeleeSpellCasted(false) && Enrage_Timer < diff) + { + DoCast(m_creature,SPELL_ENRAGE); + Enrage_Timer = 900000; + }else Enrage_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_houndmaster_loksey(Creature *_Creature) +{ + return new boss_houndmaster_lokseyAI (_Creature); +} + +void AddSC_boss_houndmaster_loksey() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_houndmaster_loksey"; + newscript->GetAI = GetAI_boss_houndmaster_loksey; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_interrogator_vishas.cpp b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_interrogator_vishas.cpp new file mode 100644 index 00000000000..c21eef4b458 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_interrogator_vishas.cpp @@ -0,0 +1,113 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Interrogator_Vishas +SD%Complete: 100 +SDComment: +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_POWERWORDSHIELD 6065 + +#define SAY_AGGRO "Tell me... tell me everything!" +#define SAY_HEALTH1 "Naughty secrets" +#define SAY_HEALTH2 "I'll rip the secrets from your flesh!" +#define SAY_DEATH "Purged by pain!" + +#define SOUND_AGGRO 5847 +#define SOUND_HEALTH1 5849 +#define SOUND_HEALTH2 5850 +#define SOUND_DEATH 5848 + +struct MANGOS_DLL_DECL boss_interrogator_vishasAI : public ScriptedAI +{ + boss_interrogator_vishasAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Yell_Timer; + uint32 PowerWordShield_Timer; + + void Reset() + { + Yell_Timer = 6000000; + PowerWordShield_Timer = 60000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are low on hp Do sayings + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 60 && !m_creature->IsNonMeleeSpellCasted(false)) + { + //Yell_Timer + if (Yell_Timer < diff) + { + DoYell(SAY_HEALTH1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_HEALTH1); + return; + + //60 seconds until we should cast this agian + Yell_Timer = 60000; + }else Yell_Timer -= diff; + } + + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 30 && !m_creature->IsNonMeleeSpellCasted(false)) + { + //Yell_Timer + if (Yell_Timer < diff) + { + DoYell(SAY_HEALTH2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_HEALTH2); + return; + + //60 seconds until we should cast this agian + Yell_Timer = 6000000; + }else Yell_Timer -= diff; + } + + //PowerWordShield_Timer + if (PowerWordShield_Timer < diff) + { + DoCast(m_creature,SPELL_POWERWORDSHIELD); + PowerWordShield_Timer = 60000; + }else PowerWordShield_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_interrogator_vishas(Creature *_Creature) +{ + return new boss_interrogator_vishasAI (_Creature); +} + +void AddSC_boss_interrogator_vishas() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_interrogator_vishas"; + newscript->GetAI = GetAI_boss_interrogator_vishas; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_scarlet_commander_mograine.cpp b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_scarlet_commander_mograine.cpp new file mode 100644 index 00000000000..6bdcda14913 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_scarlet_commander_mograine.cpp @@ -0,0 +1,160 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Scarlet_Commander_Mograine +SD%Complete: 100 +SDComment: Missing revive +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_DIVINESHIELD2 1020 +#define SPELL_CRUSADERSTRIKE5 35395 +#define SPELL_HAMMEROFJUSTICE3 5589 +#define SPELL_HOLYLIGHT6 3472 +#define SPELL_CONSECRATION3 20922 +#define SPELL_BLESSINGOFWISDOM 1044 +#define SPELL_RETRIBUTIONAURA3 10299 +#define SPELL_BLESSINGOFPROTECTION3 10278 +#define SPELL_FLASHHEAL6 10916 + +#define SAY_AGGRO "Infidels! They must be purified!" +#define SAY_RES "At your side, milady!" +#define SAY_DEATH "Unworthy!" + +#define SOUND_AGGRO 5835 +#define SOUND_RES 5837 +#define SOUND_DEATH 5836 + +struct MANGOS_DLL_DECL boss_scarlet_commander_mograineAI : public ScriptedAI +{ + boss_scarlet_commander_mograineAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Heal_Timer; + uint32 DivineShield2_Timer; + uint32 CrusaderStrike5_Timer; + uint32 HammerOfJustice3_Timer; + uint32 Consecration3_Timer; + uint32 BlessingOfWisdom_Timer; + uint32 BlessingOfProtection3_Timer; + + void Reset() + { + Heal_Timer = 80000; + DivineShield2_Timer = 60000; + CrusaderStrike5_Timer = 20000; + HammerOfJustice3_Timer = 80000; + Consecration3_Timer = 30000; + BlessingOfWisdom_Timer = 45000; + BlessingOfProtection3_Timer = 45000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + DoCast(m_creature,SPELL_RETRIBUTIONAURA3); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are <50% hp cast Arcane Bubble and start casting SPECIAL Arcane Explosion + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 50 && !m_creature->IsNonMeleeSpellCasted(false)) + { + //heal_Timer + if (Heal_Timer < diff) + { + //Switch between 2 different charge methods + switch (rand()%2) + { + case 0: + DoCast(m_creature,SPELL_HOLYLIGHT6); + break; + case 1: + DoCast(m_creature,SPELL_FLASHHEAL6); + break; + } + return; + + //60 seconds until we should cast this agian + Heal_Timer = 60000; + }else Heal_Timer -= diff; + } + + //DivineShield2_Timer + if (DivineShield2_Timer < diff) + { + DoCast(m_creature,SPELL_DIVINESHIELD2); + DivineShield2_Timer = 60000; + }else DivineShield2_Timer -= diff; + + //CrusaderStrike5_Timer + if (CrusaderStrike5_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CRUSADERSTRIKE5); + CrusaderStrike5_Timer = 20000; + }else CrusaderStrike5_Timer -= diff; + + //HammerOfJustice3_Timer + if (HammerOfJustice3_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HAMMEROFJUSTICE3); + HammerOfJustice3_Timer = 30000; + }else HammerOfJustice3_Timer -= diff; + + //Consecration3_Timer + if (Consecration3_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CONSECRATION3); + Consecration3_Timer = 20000; + }else Consecration3_Timer -= diff; + + //BlessingOfWisdom_Timer + if (BlessingOfWisdom_Timer < diff) + { + DoCast(m_creature,SPELL_BLESSINGOFWISDOM); + BlessingOfWisdom_Timer = 45000; + }else BlessingOfWisdom_Timer -= diff; + + //BlessingOfProtection3_Timer + if (BlessingOfProtection3_Timer < diff) + { + DoCast(m_creature,SPELL_BLESSINGOFPROTECTION3); + BlessingOfProtection3_Timer = 50000; + }else BlessingOfProtection3_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_scarlet_commander_mograine(Creature *_Creature) +{ + return new boss_scarlet_commander_mograineAI (_Creature); +} + +void AddSC_boss_scarlet_commander_mograine() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_scarlet_commander_mograine"; + newscript->GetAI = GetAI_boss_scarlet_commander_mograine; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_scorn.cpp b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_scorn.cpp new file mode 100644 index 00000000000..abfda1462ca --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scarlet_monastery/boss_scorn.cpp @@ -0,0 +1,100 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Scorn +SD%Complete: 100 +SDComment: +SDCategory: Scarlet Monastery +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_LICHSLAP 28873 +#define SPELL_FROSTBOLTVOLLEY 8398 +#define SPELL_MINDFLAY 17313 +#define SPELL_FROSTNOVA 15531 + +struct MANGOS_DLL_DECL boss_scornAI : public ScriptedAI +{ + boss_scornAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 LichSlap_Timer; + uint32 FrostboltVolley_Timer; + uint32 MindFlay_Timer; + uint32 FrostNova_Timer; + + void Reset() + { + LichSlap_Timer = 45000; + FrostboltVolley_Timer = 30000; + MindFlay_Timer = 30000; + FrostNova_Timer = 30000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //LichSlap_Timer + if (LichSlap_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_LICHSLAP); + LichSlap_Timer = 45000; + }else LichSlap_Timer -= diff; + + //FrostboltVolley_Timer + if (FrostboltVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROSTBOLTVOLLEY); + FrostboltVolley_Timer = 20000; + }else FrostboltVolley_Timer -= diff; + + //MindFlay_Timer + if (MindFlay_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MINDFLAY); + MindFlay_Timer = 20000; + }else MindFlay_Timer -= diff; + + //FrostNova_Timer + if (FrostNova_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROSTNOVA); + FrostNova_Timer = 15000; + }else FrostNova_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_scorn(Creature *_Creature) +{ + return new boss_scornAI (_Creature); +} + +void AddSC_boss_scorn() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_scorn"; + newscript->GetAI = GetAI_boss_scorn; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_darkmaster_gandling.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_darkmaster_gandling.cpp new file mode 100644 index 00000000000..bb4c17f3425 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_darkmaster_gandling.cpp @@ -0,0 +1,192 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Darkmaster_Gandling +SD%Complete: 99 +SDComment: Doors missing in instance script. +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_ARCANEMISSILES 22272 +#define SPELL_SHADOWSHIELD 22417 //Not right ID. But 12040 is wrong either. +#define SPELL_CURSE 18702 + +#define ADD_1X 170.205 +#define ADD_1Y 99.413 +#define ADD_1Z 104.733 +#define ADD_1O 3.16 + +#define ADD_2X 170.813 +#define ADD_2Y 97.857 +#define ADD_2Z 104.713 +#define ADD_2O 3.16 + +#define ADD_3X 170.720 +#define ADD_3Y 100.900 +#define ADD_3Z 104.739 +#define ADD_3O 3.16 + +#define ADD_4X 171.866 +#define ADD_4Y 99.373 +#define ADD_4Z 104.732 +#define ADD_4O 3.16 + +struct MANGOS_DLL_DECL boss_darkmaster_gandlingAI : public ScriptedAI +{ + boss_darkmaster_gandlingAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ArcaneMissiles_Timer; + uint32 ShadowShield_Timer; + uint32 Curse_Timer; + uint32 Teleport_Timer; + Creature *Summoned; + + void Reset() + { + ArcaneMissiles_Timer = 4500; + ShadowShield_Timer = 12000; + Curse_Timer = 2000; + Teleport_Timer = 16000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //ArcaneMissiles_Timer + if (ArcaneMissiles_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ARCANEMISSILES); + ArcaneMissiles_Timer = 8000; + }else ArcaneMissiles_Timer -= diff; + + //ShadowShield_Timer + if (ShadowShield_Timer < diff) + { + DoCast(m_creature,SPELL_SHADOWSHIELD); + ShadowShield_Timer = 14000 + rand()%14000; + }else ShadowShield_Timer -= diff; + + //Curse_Timer + if (Curse_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CURSE); + Curse_Timer = 15000 + rand()%12000; + }else Curse_Timer -= diff; + + //Teleporting Random Target to one of the six pre boss rooms and spawn 3-4 skeletons near the gamer. + //We will only telport if gandling has more than 3% of hp so teleported gamers can always loot. + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() > 3 ) + { + if(Teleport_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target && target->GetTypeId() == TYPEID_PLAYER) + { + if(m_creature->getThreatManager().getThreat(target)) + m_creature->getThreatManager().modifyThreatPercent(target, -100); + + switch(rand()%6) + { + case 0: + DoTeleportPlayer(target, 250.0696,0.3921,84.8408,3.149); + Summoned = m_creature->SummonCreature(16119,254.2325,0.3417,84.8407,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,257.7133,4.0226,84.8407,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,258.6702,-2.60656,84.8407,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + break; + case 1: + DoTeleportPlayer(target, 181.4220,-91.9481,84.8410,1.608); + Summoned = m_creature->SummonCreature(16119,184.0519,-73.5649,84.8407,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,179.5951,-73.7045,84.8407,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,180.6452,-78.2143,84.8407,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,283.2274,-78.1518,84.8407,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + break; + case 2: + DoTeleportPlayer(target, 95.1547,-1.8173,85.2289,0.043); + Summoned = m_creature->SummonCreature(16119,100.9404,-1.8016,85.2289,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,101.3729,0.4882,85.2289,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,101.4596,-4.4740,85.2289,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + break; + case 3: + DoTeleportPlayer(target, 250.0696,0.3921,72.6722,3.149); + Summoned = m_creature->SummonCreature(16119,240.34481,0.7368,72.6722,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,240.3633,-2.9520,72.6722,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,240.6702,3.34949,72.6722,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + break; + case 4: + DoTeleportPlayer(target, 181.4220,-91.9481,70.7734,1.608); + Summoned = m_creature->SummonCreature(16119,184.0519,-73.5649,70.7734,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,179.5951,-73.7045,70.7734,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,180.6452,-78.2143,70.7734,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,283.2274,-78.1518,70.7734,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + break; + case 5: + DoTeleportPlayer(target, 106.1541,-1.8994,75.3663,0.043); + Summoned = m_creature->SummonCreature(16119,115.3945,-1.5555,75.3663,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,257.7133,1.8066,75.3663,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(16119,258.6702,-5.1001,75.3663,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,10000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + break; + } + } + Teleport_Timer = 20000 + rand()%15000; + }else Teleport_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_darkmaster_gandling(Creature *_Creature) +{ + return new boss_darkmaster_gandlingAI (_Creature); +} + +void AddSC_boss_darkmaster_gandling() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_darkmaster_gandling"; + newscript->GetAI = GetAI_boss_darkmaster_gandling; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_death_knight_darkreaver.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_death_knight_darkreaver.cpp new file mode 100644 index 00000000000..344ab9840d9 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_death_knight_darkreaver.cpp @@ -0,0 +1,59 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Death_knight_darkreaver +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" + +struct MANGOS_DLL_DECL boss_death_knight_darkreaverAI : public ScriptedAI +{ + boss_death_knight_darkreaverAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if (m_creature->GetHealth() <= damage) + { + m_creature->CastSpell(m_creature,23261,true); //Summon Darkreaver's Fallen Charger + } + } + + void Aggro(Unit *who) + { + } +}; +CreatureAI* GetAI_boss_death_knight_darkreaver(Creature *_Creature) +{ + return new boss_death_knight_darkreaverAI (_Creature); +} + +void AddSC_boss_death_knight_darkreaver() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_death_knight_darkreaver"; + newscript->GetAI = GetAI_boss_death_knight_darkreaver; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_doctor_theolen_krastinov.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_doctor_theolen_krastinov.cpp new file mode 100644 index 00000000000..4dd663fbc5f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_doctor_theolen_krastinov.cpp @@ -0,0 +1,108 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Doctor_Theolen_Krastinov +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" +#include "def_scholomance.h" + +#define SPELL_REND 18106 +#define SPELL_CLEAVE 15584 +#define SPELL_FRENZY 28371 + +struct MANGOS_DLL_DECL boss_theolenkrastinovAI : public ScriptedAI +{ + boss_theolenkrastinovAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Rend_Timer; + uint32 Cleave_Timer; + uint32 Frenzy_Timer; + + void Reset() + { + Rend_Timer = 8000; + Cleave_Timer = 9000; + Frenzy_Timer =0; + } + + void JustDied(Unit *killer) + { + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + if(pInstance) + { + pInstance->SetData(DATA_DOCTORTHEOLENKRASTINOV_DEATH, 0); + + if(pInstance->GetData(DATA_CANSPAWNGANDLING)) + m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Rend_Timer + if (Rend_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_REND); + Rend_Timer = 10000; + }else Rend_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 10000; + }else Cleave_Timer -= diff; + + //Frenzy_Timer + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 26 ) + { + if (Frenzy_Timer < diff) + { + DoCast(m_creature,SPELL_FRENZY); + DoTextEmote("goes into a killing frenzy!",NULL); + + Frenzy_Timer = 8000; + }else Frenzy_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_theolenkrastinov(Creature *_Creature) +{ + return new boss_theolenkrastinovAI (_Creature); +} + +void AddSC_boss_theolenkrastinov() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_doctor_theolen_krastinov"; + newscript->GetAI = GetAI_boss_theolenkrastinov; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_illucia_barov.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_illucia_barov.cpp new file mode 100644 index 00000000000..1a45b7e2697 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_illucia_barov.cpp @@ -0,0 +1,116 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Illucia_Barov +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" +#include "def_scholomance.h" + +#define SPELL_CURSEOFAGONY 18671 +#define SPELL_SHADOWSHOCK 20603 +#define SPELL_SILENCE 15487 +#define SPELL_FEAR 6215 + +struct MANGOS_DLL_DECL boss_illuciabarovAI : public ScriptedAI +{ + boss_illuciabarovAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 CurseOfAgony_Timer; + uint32 ShadowShock_Timer; + uint32 Silence_Timer; + uint32 Fear_Timer; + + void Reset() + { + CurseOfAgony_Timer = 18000; + ShadowShock_Timer = 9000; + Silence_Timer = 5000; + Fear_Timer = 30000; + } + + void JustDied(Unit *killer) + { + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + if(pInstance) + { + pInstance->SetData(DATA_LADYILLUCIABAROV_DEATH, 0); + + if(pInstance->GetData(DATA_CANSPAWNGANDLING)) + m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //CurseOfAgony_Timer + if (CurseOfAgony_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CURSEOFAGONY); + CurseOfAgony_Timer = 30000; + }else CurseOfAgony_Timer -= diff; + + //ShadowShock_Timer + if (ShadowShock_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_SHADOWSHOCK); + + ShadowShock_Timer = 12000; + }else ShadowShock_Timer -= diff; + + //Silence_Timer + if (Silence_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SILENCE); + Silence_Timer = 14000; + }else Silence_Timer -= diff; + + //Fear_Timer + if (Fear_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FEAR); + Fear_Timer = 30000; + }else Fear_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_illuciabarov(Creature *_Creature) +{ + return new boss_illuciabarovAI (_Creature); +} + +void AddSC_boss_illuciabarov() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_illucia_barov"; + newscript->GetAI = GetAI_boss_illuciabarov; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_instructor_malicia.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_instructor_malicia.cpp new file mode 100644 index 00000000000..9d2dbc174d5 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_instructor_malicia.cpp @@ -0,0 +1,152 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_instructormalicia +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" +#include "def_scholomance.h" + +#define SPELL_CALLOFGRAVES 17831 +#define SPELL_CORRUPTION 11672 +#define SPELL_FLASHHEAL 10917 +#define SPELL_RENEW 10929 +#define SPELL_HEALINGTOUCH 9889 + +struct MANGOS_DLL_DECL boss_instructormaliciaAI : public ScriptedAI +{ + boss_instructormaliciaAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 CallOfGraves_Timer; + uint32 Corruption_Timer; + uint32 FlashHeal_Timer; + uint32 Renew_Timer; + uint32 HealingTouch_Timer; + uint32 FlashCounter; + uint32 TouchCounter; + + void Reset() + { + CallOfGraves_Timer = 4000; + Corruption_Timer = 8000; + FlashHeal_Timer = 38000; + Renew_Timer = 32000; + HealingTouch_Timer = 45000; + FlashCounter = 0; + TouchCounter = 0; + } + + void JustDied(Unit *killer) + { + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + if(pInstance) + { + pInstance->SetData(DATA_INSTRUCTORMALICIA_DEATH, 0); + + if(pInstance->GetData(DATA_CANSPAWNGANDLING)) + m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //CallOfGraves_Timer + if (CallOfGraves_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CALLOFGRAVES); + CallOfGraves_Timer = 65000; + }else CallOfGraves_Timer -= diff; + + //Corruption_Timer + if (Corruption_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_CORRUPTION); + + Corruption_Timer = 24000; + }else Corruption_Timer -= diff; + + //Renew_Timer + if (Renew_Timer < diff) + { + DoCast(m_creature, SPELL_RENEW); + Renew_Timer = 10000; + }else Renew_Timer -= diff; + + //FlashHeal_Timer + if (FlashHeal_Timer < diff) + { + DoCast(m_creature,SPELL_FLASHHEAL); + + //5 Flashheals will be casted + if (FlashCounter < 2) + { + FlashHeal_Timer = 5000; + FlashCounter++; + } + else + { + FlashCounter=0; + FlashHeal_Timer = 30000; + } + }else FlashHeal_Timer -= diff; + + //HealingTouch_Timer + if (HealingTouch_Timer < diff) + { + DoCast(m_creature,SPELL_HEALINGTOUCH); + + //3 Healingtouchs will be casted + if (HealingTouch_Timer < 2) + { + HealingTouch_Timer = 5500; + TouchCounter++; + } + else + { + TouchCounter=0; + HealingTouch_Timer = 30000; + } + }else HealingTouch_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_instructormalicia(Creature *_Creature) +{ + return new boss_instructormaliciaAI (_Creature); +} + +void AddSC_boss_instructormalicia() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_instructor_malicia"; + newscript->GetAI = GetAI_boss_instructormalicia; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_jandice_barov.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_jandice_barov.cpp new file mode 100644 index 00000000000..05eaba72a3f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_jandice_barov.cpp @@ -0,0 +1,226 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_jandicebarov +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_CURSEOFBLOOD 24673 +//#define SPELL_ILLUSION 17773 + +//Spells of Illusion of Jandice Barov +#define SPELL_CLEAVE 15584 + +struct MANGOS_DLL_DECL boss_jandicebarovAI : public ScriptedAI +{ + boss_jandicebarovAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 CurseOfBlood_Timer; + uint32 Illusion_Timer; + //uint32 Illusioncounter; + uint32 Invisible_Timer; + bool Invisible; + int Rand; + int RandX; + int RandY; + Creature* Summoned; + + void Reset() + { + CurseOfBlood_Timer = 15000; + Illusion_Timer = 30000; + Invisible_Timer = 3000; //Too much too low? + Invisible = false; + } + + void Aggro(Unit *who) + { + } + + void SummonIllusions(Unit* victim) + { + Rand = rand()%10; + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = rand()%10; + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + Summoned = DoSpawnCreature(11439, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000); + if(Summoned) + ((CreatureAI*)Summoned->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + if (Invisible && Invisible_Timer < diff) + { + //Become visible again + m_creature->setFaction(14); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + //Jandice Model + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,11073); + Invisible = false; + } else if (Invisible) + { + Invisible_Timer -= diff; + //Do nothing while invisible + return; + } + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //CurseOfBlood_Timer + if (CurseOfBlood_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CURSEOFBLOOD); + CurseOfBlood_Timer = 30000; + }else CurseOfBlood_Timer -= diff; + + //Illusion_Timer + if (!Invisible && Illusion_Timer < diff) + { + //Inturrupt any spell casting + m_creature->InterruptNonMeleeSpells(false); + m_creature->setFaction(35); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + // Invisible Model + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,11686); + + //Summon 10 Illusions attacking random gamers + Unit* target = NULL; + for(int i = 0; i < 10;i++) + { + target = SelectUnit(SELECT_TARGET_RANDOM,0); + SummonIllusions(target); + } + Invisible = true; + Invisible_Timer = 3000; + + Illusion_Timer = 25000; + }else Illusion_Timer -= diff; + + // //Illusion_Timer + // if (Illusion_Timer < diff) + // { + // //Cast + // DoCast(m_creature->getVictim(),SPELL_ILLUSION); + // + // //3 Illusion will be summoned + // if (Illusioncounter < 3) + // { + // Illusion_Timer = 500; + // Illusioncounter++; + // } + // else { + // Illusion_Timer = 15000; + // Illusioncounter=0; + // } + // + // }else Illusion_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +// Illusion of Jandice Barov Script + +struct MANGOS_DLL_DECL mob_illusionofjandicebarovAI : public ScriptedAI +{ + mob_illusionofjandicebarovAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Cleave_Timer; + + void Reset() + { + Cleave_Timer = 4000; + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 5000 + rand()%3000; + }else Cleave_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_jandicebarov(Creature *_Creature) +{ + return new boss_jandicebarovAI (_Creature); +} + +CreatureAI* GetAI_mob_illusionofjandicebarov(Creature *_Creature) +{ + return new mob_illusionofjandicebarovAI (_Creature); +} + +void AddSC_boss_jandicebarov() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_jandice_barov"; + newscript->GetAI = GetAI_boss_jandicebarov; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_illusionofjandicebarov"; + newscript->GetAI = GetAI_mob_illusionofjandicebarov; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_kormok.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_kormok.cpp new file mode 100644 index 00000000000..c9fb3c3a260 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_kormok.cpp @@ -0,0 +1,155 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Kormok +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOWBOLTVOLLEY 20741 +#define SPELL_BONESHIELD 27688 + +struct MANGOS_DLL_DECL boss_kormokAI : public ScriptedAI +{ + boss_kormokAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowVolley_Timer; + uint32 BoneShield_Timer; + uint32 Minion_Timer; + uint32 Mage_Timer; + bool Mages; + int Rand1; + int Rand1X; + int Rand1Y; + int Rand2; + int Rand2X; + int Rand2Y; + Creature* SummonedMinions; + Creature* SummonedMages; + + void Reset() + { + ShadowVolley_Timer = 10000; + BoneShield_Timer = 2000; + Minion_Timer = 15000; + Mage_Timer = 0; + Mages = false; + } + + void Aggro(Unit *who) + { + } + + void SummonMinion(Unit* victim) + { + Rand1 = rand()%8; + switch (rand()%2) + { + case 0: Rand1X = 0 - Rand1; break; + case 1: Rand1X = 0 + Rand1; break; + } + Rand1 = 0; + Rand1 = rand()%8; + switch (rand()%2) + { + case 0: Rand1Y = 0 - Rand1; break; + case 1: Rand1Y = 0 + Rand1; break; + } + Rand1 = 0; + SummonedMinions = DoSpawnCreature(16119, Rand1X, Rand1Y, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 120000); + ((CreatureAI*)SummonedMinions->AI())->AttackStart(victim); + } + + void SummonMages(Unit* victim) + { + Rand2 = rand()%10; + switch (rand()%2) + { + case 0: Rand2X = 0 - Rand2; break; + case 1: Rand2X = 0 + Rand2; break; + } + Rand2 = 0; + Rand2 = rand()%10; + switch (rand()%2) + { + case 0: Rand2Y = 0 - Rand2; break; + case 1: Rand2Y = 0 + Rand2; break; + } + Rand2 = 0; + SummonedMages = DoSpawnCreature(16120, Rand2X, Rand2Y, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 120000); + ((CreatureAI*)SummonedMages->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //ShadowVolley_Timer + if (ShadowVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWBOLTVOLLEY); + ShadowVolley_Timer = 15000; + }else ShadowVolley_Timer -= diff; + + //BoneShield_Timer + if (BoneShield_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_BONESHIELD); + BoneShield_Timer = 45000; + }else BoneShield_Timer -= diff; + + //Minion_Timer + if (Minion_Timer < diff) + { + //Cast + SummonMinion(m_creature->getVictim()); + SummonMinion(m_creature->getVictim()); + SummonMinion(m_creature->getVictim()); + SummonMinion(m_creature->getVictim()); + + Minion_Timer = 12000; + }else Minion_Timer -= diff; + + //Summon 2 Bone Mages + if ( !Mages && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 26 ) + { + //Cast + SummonMages(m_creature->getVictim()); + SummonMages(m_creature->getVictim()); + Mages = true; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_kormok(Creature *_Creature) +{ + return new boss_kormokAI (_Creature); +} + +void AddSC_boss_kormok() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_kormok"; + newscript->GetAI = GetAI_boss_kormok; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_lord_alexei_barov.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_lord_alexei_barov.cpp new file mode 100644 index 00000000000..65601ff3ee3 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_lord_alexei_barov.cpp @@ -0,0 +1,98 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Lord_Alexei_Barov +SD%Complete: 100 +SDComment: aura applied/defined in database +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" +#include "def_scholomance.h" + +#define SPELL_IMMOLATE 20294 // Old ID was 15570 +#define SPELL_VEILOFSHADOW 17820 + +struct MANGOS_DLL_DECL boss_lordalexeibarovAI : public ScriptedAI +{ + boss_lordalexeibarovAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Immolate_Timer; + uint32 VeilofShadow_Timer; + + void Reset() + { + Immolate_Timer = 7000; + VeilofShadow_Timer = 15000; + + m_creature->LoadCreaturesAddon(); + } + + void JustDied(Unit *killer) + { + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + if(pInstance) + { + pInstance->SetData(DATA_LORDALEXEIBAROV_DEATH, 0); + + if(pInstance->GetData(DATA_CANSPAWNGANDLING)) + m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Immolate_Timer + if (Immolate_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_IMMOLATE); + + Immolate_Timer = 12000; + }else Immolate_Timer -= diff; + + //VeilofShadow_Timer + if (VeilofShadow_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_VEILOFSHADOW); + VeilofShadow_Timer = 20000; + }else VeilofShadow_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_lordalexeibarov(Creature *_Creature) +{ + return new boss_lordalexeibarovAI (_Creature); +} + +void AddSC_boss_lordalexeibarov() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_lord_alexei_barov"; + newscript->GetAI = GetAI_boss_lordalexeibarov; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_lorekeeper_polkelt.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_lorekeeper_polkelt.cpp new file mode 100644 index 00000000000..c1d6929ddfa --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_lorekeeper_polkelt.cpp @@ -0,0 +1,113 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Lorekeeper_Polkelt +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" +#include "def_scholomance.h" + +#define SPELL_VOLATILEINFECTION 24928 +#define SPELL_DARKPLAGUE 18270 +#define SPELL_CORROSIVEACID 23313 +#define SPELL_NOXIOUSCATALYST 18151 + +struct MANGOS_DLL_DECL boss_lorekeeperpolkeltAI : public ScriptedAI +{ + boss_lorekeeperpolkeltAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 VolatileInfection_Timer; + uint32 Darkplague_Timer; + uint32 CorrosiveAcid_Timer; + uint32 NoxiousCatalyst_Timer; + + void Reset() + { + VolatileInfection_Timer = 38000; + Darkplague_Timer = 8000; + CorrosiveAcid_Timer = 45000; + NoxiousCatalyst_Timer = 35000; + } + + void JustDied(Unit *killer) + { + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + if(pInstance) + { + pInstance->SetData(DATA_LOREKEEPERPOLKELT_DEATH, 0); + + if(pInstance->GetData(DATA_CANSPAWNGANDLING)) + m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //VolatileInfection_Timer + if (VolatileInfection_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_VOLATILEINFECTION); + VolatileInfection_Timer = 32000; + }else VolatileInfection_Timer -= diff; + + //Darkplague_Timer + if (Darkplague_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_DARKPLAGUE); + Darkplague_Timer = 8000; + }else Darkplague_Timer -= diff; + + //CorrosiveAcid_Timer + if (CorrosiveAcid_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CORROSIVEACID); + CorrosiveAcid_Timer = 25000; + }else CorrosiveAcid_Timer -= diff; + + //NoxiousCatalyst_Timer + if (NoxiousCatalyst_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_NOXIOUSCATALYST); + NoxiousCatalyst_Timer = 38000; + }else NoxiousCatalyst_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_lorekeeperpolkelt(Creature *_Creature) +{ + return new boss_lorekeeperpolkeltAI (_Creature); +} + +void AddSC_boss_lorekeeperpolkelt() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_lorekeeper_polkelt"; + newscript->GetAI = GetAI_boss_lorekeeperpolkelt; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_ras_frostwhisper.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_ras_frostwhisper.cpp new file mode 100644 index 00000000000..f8d9e6a66b8 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_ras_frostwhisper.cpp @@ -0,0 +1,125 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Ras_Frostwhisper +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FROSTBOLT 21369 +#define SPELL_ICEARMOR 18100 //This is actually a buff he gives himself +#define SPELL_FREEZE 18763 +#define SPELL_FEAR 26070 +#define SPELL_CHILLNOVA 18099 +#define SPELL_FROSTVOLLEY 8398 + +struct MANGOS_DLL_DECL boss_rasfrostAI : public ScriptedAI +{ + boss_rasfrostAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 IceArmor_Timer; + uint32 Frostbolt_Timer; + uint32 Freeze_Timer; + uint32 Fear_Timer; + uint32 ChillNova_Timer; + uint32 FrostVolley_Timer; + + void Reset() + { + IceArmor_Timer = 2000; + Frostbolt_Timer = 8000; + ChillNova_Timer = 12000; + Freeze_Timer = 18000; + FrostVolley_Timer = 24000; + Fear_Timer = 45000; + + m_creature->CastSpell(m_creature,SPELL_ICEARMOR,true); + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //IceArmor_Timer + if (IceArmor_Timer < diff) + { + DoCast(m_creature, SPELL_ICEARMOR); + IceArmor_Timer = 180000; + }else IceArmor_Timer -= diff; + + //Frostbolt_Timer + if (Frostbolt_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_FROSTBOLT); + + Frostbolt_Timer = 8000; + }else Frostbolt_Timer -= diff; + + //Freeze_Timer + if (Freeze_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FREEZE); + Freeze_Timer = 24000; + }else Freeze_Timer -= diff; + + //Fear_Timer + if (Fear_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FEAR); + Fear_Timer = 30000; + }else Fear_Timer -= diff; + + //ChillNova_Timer + if (ChillNova_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CHILLNOVA); + ChillNova_Timer = 14000; + }else ChillNova_Timer -= diff; + + //FrostVolley_Timer + if (FrostVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROSTVOLLEY); + FrostVolley_Timer = 15000; + }else FrostVolley_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_rasfrost(Creature *_Creature) +{ + return new boss_rasfrostAI (_Creature); +} + +void AddSC_boss_rasfrost() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_boss_ras_frostwhisper"; + newscript->GetAI = GetAI_boss_rasfrost; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_the_ravenian.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_the_ravenian.cpp new file mode 100644 index 00000000000..ae833b8cc1c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_the_ravenian.cpp @@ -0,0 +1,118 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_the_ravenian +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" +#include "def_scholomance.h" + +#define SPELL_TRAMPLE 15550 +#define SPELL_CLEAVE 20691 +#define SPELL_SUNDERINCLEAVE 25174 +#define SPELL_KNOCKAWAY 10101 + +#define SAY_AGGRO1 "Mine! Mine! Mine! Gizlock is the ruler of this domain! You shall never reveal my presence!" + +struct MANGOS_DLL_DECL boss_theravenianAI : public ScriptedAI +{ + boss_theravenianAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Trample_Timer; + uint32 Cleave_Timer; + uint32 SunderingCleave_Timer; + uint32 KnockAway_Timer; + bool HasYelled; + + void Reset() + { + Trample_Timer = 24000; + Cleave_Timer = 15000; + SunderingCleave_Timer = 40000; + KnockAway_Timer = 32000; + HasYelled = false; + } + + void JustDied(Unit *killer) + { + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + if(pInstance) + { + pInstance->SetData(DATA_THERAVENIAN_DEATH, 0); + + if(pInstance->GetData(DATA_CANSPAWNGANDLING)) + m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0); + } + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO1, LANG_UNIVERSAL, NULL); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Trample_Timer + if (Trample_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_TRAMPLE); + Trample_Timer = 10000; + }else Trample_Timer -= diff; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 7000; + }else Cleave_Timer -= diff; + + //SunderingCleave_Timer + if (SunderingCleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SUNDERINCLEAVE); + SunderingCleave_Timer = 20000; + }else SunderingCleave_Timer -= diff; + + //KnockAway_Timer + if (KnockAway_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCKAWAY); + KnockAway_Timer = 12000; + }else KnockAway_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_theravenian(Creature *_Creature) +{ + return new boss_theravenianAI (_Creature); +} + +void AddSC_boss_theravenian() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_the_ravenian"; + newscript->GetAI = GetAI_boss_theravenian; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/boss_vectus.cpp b/src/bindings/scripts/scripts/zone/scholomance/boss_vectus.cpp new file mode 100644 index 00000000000..d6808efaf8a --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/boss_vectus.cpp @@ -0,0 +1,95 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Vectus +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FIRESHIELD 19626 +#define SPELL_BLASTWAVE 13021 +#define SPELL_FRENZY 28371 + +struct MANGOS_DLL_DECL boss_vectusAI : public ScriptedAI +{ + boss_vectusAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FireShield_Timer; + uint32 BlastWave_Timer; + uint32 Frenzy_Timer; + + void Reset() + { + FireShield_Timer = 2000; + BlastWave_Timer = 14000; + Frenzy_Timer = 0; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //FireShield_Timer + if (FireShield_Timer < diff) + { + DoCast(m_creature, SPELL_FIRESHIELD); + FireShield_Timer = 90000; + }else FireShield_Timer -= diff; + + //BlastWave_Timer + if (BlastWave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_BLASTWAVE); + BlastWave_Timer = 12000; + }else BlastWave_Timer -= diff; + + //Frenzy_Timer + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 25 ) + { + if (Frenzy_Timer < diff) + { + DoCast(m_creature,SPELL_FRENZY); + DoTextEmote("goes into a killing frenzy!",NULL); + + Frenzy_Timer = 24000; + }else Frenzy_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_vectus(Creature *_Creature) +{ + return new boss_vectusAI (_Creature); +} + +void AddSC_boss_vectus() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_vectus"; + newscript->GetAI = GetAI_boss_vectus; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/scholomance/def_scholomance.h b/src/bindings/scripts/scripts/zone/scholomance/def_scholomance.h new file mode 100644 index 00000000000..a4023315e01 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/def_scholomance.h @@ -0,0 +1,15 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SCHOLOMANCE_H +#define DEF_SCHOLOMANCE_H + +#define DATA_CANSPAWNGANDLING 1 +#define DATA_DOCTORTHEOLENKRASTINOV_DEATH 2 +#define DATA_INSTRUCTORMALICIA_DEATH 3 +#define DATA_LADYILLUCIABAROV_DEATH 4 +#define DATA_LORDALEXEIBAROV_DEATH 5 +#define DATA_LOREKEEPERPOLKELT_DEATH 6 +#define DATA_THERAVENIAN_DEATH 7 +#endif diff --git a/src/bindings/scripts/scripts/zone/scholomance/instance_scholomance.cpp b/src/bindings/scripts/scripts/zone/scholomance/instance_scholomance.cpp new file mode 100644 index 00000000000..7084635493c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/scholomance/instance_scholomance.cpp @@ -0,0 +1,102 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Scholomance +SD%Complete: 100 +SDComment: +SDCategory: Scholomance +EndScriptData */ + +#include "precompiled.h" +#include "def_scholomance.h" + +struct MANGOS_DLL_DECL instance_scholomance : public ScriptedInstance +{ + instance_scholomance(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + //Lord Alexei Barov, Doctor Theolen Krastinov, The Ravenian, Lorekeeper Polkelt, Instructor Malicia and the Lady Illucia Barov. + bool IsBossDied[6]; + + void Initialize() + { + IsBossDied[0] = false; + IsBossDied[1] = false; + IsBossDied[2] = false; + IsBossDied[3] = false; + IsBossDied[4] = false; + IsBossDied[5] = false; + } + + bool IsEncounterInProgress() const + { + //not active in scholomance + return false; + } + + uint32 GetData(uint32 type) + { + if(type == DATA_CANSPAWNGANDLING) + if(IsBossDied[0] && IsBossDied[1] && IsBossDied[2] && IsBossDied[3] && IsBossDied[4] && IsBossDied[5]) + return 1; + + return 0; + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_LORDALEXEIBAROV_DEATH: + IsBossDied[0] = true; + break; + + case DATA_DOCTORTHEOLENKRASTINOV_DEATH: + IsBossDied[1] = true; + break; + + case DATA_THERAVENIAN_DEATH: + IsBossDied[2] = true; + break; + + case DATA_LOREKEEPERPOLKELT_DEATH: + IsBossDied[3] = true; + break; + + case DATA_INSTRUCTORMALICIA_DEATH: + IsBossDied[4] = true; + break; + + case DATA_LADYILLUCIABAROV_DEATH: + IsBossDied[5] = true; + break; + } + } +}; + +InstanceData* GetInstanceData_instance_scholomance(Map* map) +{ + return new instance_scholomance(map); +} + +void AddSC_instance_scholomance() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_scholomance"; + newscript->GetInstanceData = GetInstanceData_instance_scholomance; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/searing_gorge/searing_gorge.cpp b/src/bindings/scripts/scripts/zone/searing_gorge/searing_gorge.cpp new file mode 100644 index 00000000000..6834cd8a078 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/searing_gorge/searing_gorge.cpp @@ -0,0 +1,159 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Searing_Gorge +SD%Complete: 80 +SDComment: Quest support: 3377, 3441 (More accurate info on Kalaran needed). Lothos Riftwaker teleport to Molten Core. +SDCategory: Searing Gorge +EndScriptData */ + +/* ContentData +npc_kalaran_windblade +npc_lothos_riftwaker +npc_zamael_lunthistle +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_kalaran_windblade +######*/ + +bool GossipHello_npc_kalaran_windblade(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(3441) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Tell me what drives this vengance?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_kalaran_windblade(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, "Continue please", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(1954, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "Let me confer with my colleagues", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(1955, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(3441); + break; + } + return true; +} + +/*###### +## npc_lothos_riftwaker +######*/ + +bool GossipHello_npc_lothos_riftwaker(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestRewardStatus(7487) || player->GetQuestRewardStatus(7848)) + player->ADD_GOSSIP_ITEM(0, "Teleport me to the Molten Core", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_lothos_riftwaker(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + player->CLOSE_GOSSIP_MENU(); + player->TeleportTo(409, 1096, -467, -104.6, 3.64); + } + + return true; +} + +/*###### +## npc_zamael_lunthistle +######*/ + +bool GossipHello_npc_zamael_lunthistle(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(3377) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Tell me your story", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(1920, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_zamael_lunthistle(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, "Please continue...", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(1921, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "Goodbye", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(1922, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(3377); + break; + } + return true; +} + +/*###### +## +######*/ + +void AddSC_searing_gorge() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_kalaran_windblade"; + newscript->pGossipHello = &GossipHello_npc_kalaran_windblade; + newscript->pGossipSelect = &GossipSelect_npc_kalaran_windblade; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_lothos_riftwaker"; + newscript->pGossipHello = &GossipHello_npc_lothos_riftwaker; + newscript->pGossipSelect = &GossipSelect_npc_lothos_riftwaker; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_zamael_lunthistle"; + newscript->pGossipHello = &GossipHello_npc_zamael_lunthistle; + newscript->pGossipSelect = &GossipSelect_npc_zamael_lunthistle; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/shadowfang_keep/def_shadowfang_keep.h b/src/bindings/scripts/scripts/zone/shadowfang_keep/def_shadowfang_keep.h new file mode 100644 index 00000000000..7ece07f0231 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/shadowfang_keep/def_shadowfang_keep.h @@ -0,0 +1,12 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SHADOWFANG_H +#define DEF_SHADOWFANG_H + +#define TYPE_FREE_NPC 1 +#define TYPE_RETHILGORE 2 +#define TYPE_FENRUS 3 +#define TYPE_NANDOS 4 +#endif diff --git a/src/bindings/scripts/scripts/zone/shadowfang_keep/instance_shadowfang_keep.cpp b/src/bindings/scripts/scripts/zone/shadowfang_keep/instance_shadowfang_keep.cpp new file mode 100644 index 00000000000..5e4ce0b3267 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/shadowfang_keep/instance_shadowfang_keep.cpp @@ -0,0 +1,152 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Shadowfang_Keep +SD%Complete: 75 +SDComment: TODO: check what other parts would require additional code (ex: make sure door are in open state if boss dead) +SDCategory: Shadowfang Keep +EndScriptData */ + +#include "precompiled.h" +#include "def_shadowfang_keep.h" + +#define ENCOUNTERS 4 + +//#define ENTRY_BOSS_RETHILGORE 3914 +//#define ENTRY_BOSS_FENRUS 4274 +//#define ENTRY_BOSS_NANDOS 3927 + +#define ENTRY_COURTYARD_DOOR 18895 //door to open when talking to NPC's +#define ENTRY_SORCERER_DOOR 18972 //door to open when Fenrus the Devourer +#define ENTRY_ARUGAL_DOOR 18971 //door to open when Wolf Master Nandos + +struct MANGOS_DLL_DECL instance_shadowfang_keep : public ScriptedInstance +{ + instance_shadowfang_keep(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint32 Encounter[ENCOUNTERS]; + + /*uint64 RethilgoreGUID; + uint64 FenrusGUID; + uint64 NandosGUID;*/ + + GameObject *DoorCourtyard; + GameObject *DoorSorcerer; + GameObject *DoorArugal; + + void Initialize() + { + /*RethilgoreGUID = 0; + FenrusGUID = 0; + NandosGUID = 0;*/ + + DoorCourtyard = NULL; + DoorSorcerer = NULL; + DoorArugal = NULL; + } + + void OnObjectCreate(GameObject *go) + { + switch(go->GetEntry()) + { + case ENTRY_COURTYARD_DOOR: DoorCourtyard = go; break; + case ENTRY_SORCERER_DOOR: DoorSorcerer = go; break; + case ENTRY_ARUGAL_DOOR: DoorArugal = go; break; + } + } + + /*void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case ENTRY_BOSS_RETHILGORE: + RethilgoreGUID = creature->GetGUID(); + break; + case ENTRY_BOSS_FENRUS: + FenrusGUID = creature->GetGUID(); + break; + case ENTRY_BOSS_NANDOS: + NandosGUID = creature->GetGUID(); + break; + } + }*/ + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case TYPE_FREE_NPC: + if(data == DONE) + { + if(DoorCourtyard) + DoorCourtyard->UseDoorOrButton(); + } + Encounter[0] = data; + break; + case TYPE_RETHILGORE: + Encounter[1] = data; + break; + case TYPE_FENRUS: + if(data == DONE) + { + if(DoorSorcerer) + DoorSorcerer->UseDoorOrButton(); + } + Encounter[2] = data; + break; + case TYPE_NANDOS: + if(data == DONE) + { + if(DoorArugal) + DoorArugal->UseDoorOrButton(); + } + Encounter[3] = data; + break; + } + } + + uint32 GetData(uint32 type) + { + switch(type) + { + case TYPE_FREE_NPC: + return Encounter[0]; + case TYPE_RETHILGORE: + return Encounter[1]; + case TYPE_FENRUS: + return Encounter[2]; + case TYPE_NANDOS: + return Encounter[3]; + } + return 0; + } + +}; + +InstanceData* GetInstanceData_instance_shadowfang_keep(Map* map) +{ + return new instance_shadowfang_keep(map); +} + +void AddSC_instance_shadowfang_keep() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_shadowfang_keep"; + newscript->GetInstanceData = GetInstanceData_instance_shadowfang_keep; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/shadowfang_keep/shadowfang_keep.cpp b/src/bindings/scripts/scripts/zone/shadowfang_keep/shadowfang_keep.cpp new file mode 100644 index 00000000000..172a0907a79 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/shadowfang_keep/shadowfang_keep.cpp @@ -0,0 +1,117 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Shadowfang_Keep +SD%Complete: 75 +SDComment: npc_shadowfang_prisoner using escortAI for movement to door. Might need additional code in case being attacked. Add proper texts/say(). +SDCategory: Shadowfang Keep +EndScriptData */ + +/* ContentData +npc_shadowfang_prisoner +EndContentData */ + +#include "precompiled.h" +#include "../../npc/npc_escortAI.h" +#include "def_shadowfang_keep.h" + +/*###### +## npc_shadowfang_prisoner +######*/ + +struct MANGOS_DLL_DECL npc_shadowfang_prisonerAI : public npc_escortAI +{ + npc_shadowfang_prisonerAI(Creature *c) : npc_escortAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + void WaypointReached(uint32 i) + { + if( pInstance && i == 6) + { + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + m_creature->Say("Thanks for freeing me, I'll open this door for you, then I will get out of here.", LANG_UNIVERSAL, 0); + pInstance->SetData(TYPE_FREE_NPC, DONE); + } + } + + void Reset() {} + void Aggro(Unit* who) {} +}; + +CreatureAI* GetAI_npc_shadowfang_prisoner(Creature *_Creature) +{ + npc_shadowfang_prisonerAI* prisonerAI = new npc_shadowfang_prisonerAI(_Creature); + + uint32 eCreature = _Creature->GetEntry(); + + if( eCreature==3849) //adamant + prisonerAI->AddWaypoint(0, -254.47, 2117.48, 81.17); + if( eCreature==3850) //ashcrombe + prisonerAI->AddWaypoint(0, -252.35, 2126.71, 81.17); + + prisonerAI->AddWaypoint(1, -253.63, 2131.27, 81.28); + prisonerAI->AddWaypoint(2, -249.66, 2142.45, 87.01); + prisonerAI->AddWaypoint(3, -248.08, 2143.68, 87.01); + prisonerAI->AddWaypoint(4, -238.87, 2139.93, 87.01); + prisonerAI->AddWaypoint(5, -235.47, 2149.18, 90.59); + prisonerAI->AddWaypoint(6, -239.89, 2156.06, 90.62, 20000); + + return (CreatureAI*)prisonerAI; +} + +bool GossipHello_npc_shadowfang_prisoner(Player *player, Creature *_Creature) +{ + ScriptedInstance* pInstance = ((ScriptedInstance*)_Creature->GetInstanceData()); + + if( pInstance && !pInstance->GetData(TYPE_FREE_NPC) && pInstance->GetData(TYPE_RETHILGORE) == DONE ) + player->ADD_GOSSIP_ITEM( 0, "Thanks, I'll follow you to the door.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_shadowfang_prisoner(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + player->CLOSE_GOSSIP_MENU(); + ((npc_escortAI*)(_Creature->AI()))->Start(false, false, false, player->GetGUID()); + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_shadowfang_keep() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_shadowfang_prisoner"; + newscript->pGossipHello = &GossipHello_npc_shadowfang_prisoner; + newscript->pGossipSelect = &GossipSelect_npc_shadowfang_prisoner; + newscript->GetAI = GetAI_npc_shadowfang_prisoner; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/shadowmoon_valley/boss_doomwalker.cpp b/src/bindings/scripts/scripts/zone/shadowmoon_valley/boss_doomwalker.cpp new file mode 100644 index 00000000000..1960d9f9cc1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/shadowmoon_valley/boss_doomwalker.cpp @@ -0,0 +1,211 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Doomwalker +SD%Complete: 100 +SDComment: +SDCategory: Shadowmoon Valley +EndScriptData */ + +#include "precompiled.h" + +//-------------------------------------- +//Spells +#define SPELL_SUNDER_ARMOR 30901 + +#define SPELL_CHAIN_LIGHTNING 33665 + +#define SPELL_OVERRUN 32636 +#define SAY_OVERRUN_1 "Trajectory locked." +#define SOUND_OVERRUN_1 11347 +#define SAY_OVERRUN_2 "Engage maximum speed." +#define SOUND_OVERRUN_2 11348 + +#define SPELL_ENRAGE 34624 + +#define SPELL_MARK_DEATH 37128 + +#define SPELL_EARTHQUAKE 32686 +#define SAY_EARTHQUAKE_1 "Tectonic disruption commencing." +#define SOUND_EARTHQUAKE_1 11345 +#define SAY_EARTHQUAKE_2 "Magnitude set. Release." +#define SOUND_EARTHQUAKE_2 11346 + +#define SAY_AGGRO "Do not proceed. You will be eliminated!" +#define SOUND_AGGRO 11344 + +#define SAY_SLAY_1 "Threat level zero." +#define SOUND_SLAY_1 11349 +#define SAY_SLAY_2 "Directive accomplished." +#define SOUND_SLAY_2 11350 +#define SAY_SLAY_3 "Target exterminated." +#define SOUND_SLAY_3 11351 + +#define SAY_DEATH "System failure in five... four..." +#define SOUND_DEATH 11352 + +struct MANGOS_DLL_DECL boss_doomwalkerAI : public ScriptedAI +{ + boss_doomwalkerAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Chain_Timer; + uint32 Enrage_Timer; + uint32 Overrun_Timer; + uint32 Quake_Timer; + uint32 Armor_Timer; + + bool InEnrage; + + void Reset() + { + Enrage_Timer = 0; + Armor_Timer = 10000; + Chain_Timer = 20000; + Quake_Timer = 60000; + Overrun_Timer = 120000; + + InEnrage = false; + } + + void KilledUnit(Unit* Victim) + { + if(rand()%5) + return; + + switch(rand()%3) + { + case 0: + DoYell(SAY_SLAY_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY_1); + break; + case 1: + DoYell(SAY_SLAY_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY_2); + break; + case 2: + DoYell(SAY_SLAY_3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY_3); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + { + //when hp <= 20% gain enrage + if (((m_creature->GetHealth()*100)/ m_creature->GetMaxHealth()) <= 20) + { + if(Enrage_Timer < diff) + { + DoCast(m_creature,SPELL_ENRAGE); + Enrage_Timer = 6000; + InEnrage = true; + }else Enrage_Timer -= diff; + } + + //Spell Overrun + if (Overrun_Timer < diff) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_OVERRUN_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_OVERRUN_1); + break; + case 1: + DoYell(SAY_OVERRUN_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_OVERRUN_2); + break; + } + DoCast(m_creature->getVictim(),SPELL_OVERRUN); + Overrun_Timer = (30 + rand()% 40) * 1000; //30-70sec cooldown + + }else Overrun_Timer -= diff; + + //Spell Earthquake + if (Quake_Timer < diff) + { + if (rand()%2) + return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_EARTHQUAKE_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_EARTHQUAKE_1); + break; + case 1: + DoYell(SAY_EARTHQUAKE_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_EARTHQUAKE_2); + break; + } + if(InEnrage) + { + m_creature->RemoveAura(SPELL_ENRAGE, 0);//remove enrage before casting earthquake because enrage + earthquake = 16000dmg over 8sec and all dead + } + DoCast(m_creature,SPELL_EARTHQUAKE); + Quake_Timer = (70 + rand()% 30) * 1000; //70-100sec cooldown + }else Quake_Timer -= diff; + + //Spell Chain Lightning + if (Chain_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CHAIN_LIGHTNING); + Chain_Timer = (50 + rand()% 50) * 1000; //50-100sec cooldown + }else Chain_Timer -= diff; + + //Spell Sunder Armor + if (Armor_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SUNDER_ARMOR); + Armor_Timer = (15 + rand()% 7) * 1000; //15-23sec cooldown about 70 proc to stack + }else Armor_Timer -= diff; + + DoMeleeAttackIfReady(); + } + } +}; + +CreatureAI* GetAI_boss_doomwalker(Creature *_Creature) +{ + return new boss_doomwalkerAI (_Creature); +} + +void AddSC_boss_doomwalker() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_doomwalker"; + newscript->GetAI = GetAI_boss_doomwalker; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/shadowmoon_valley/shadowmoon_valley.cpp b/src/bindings/scripts/scripts/zone/shadowmoon_valley/shadowmoon_valley.cpp new file mode 100644 index 00000000000..d75d50c666b --- /dev/null +++ b/src/bindings/scripts/scripts/zone/shadowmoon_valley/shadowmoon_valley.cpp @@ -0,0 +1,748 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Shadowmoon_Valley +SD%Complete: 100 +SDComment: Quest support: 10519, 10583, 10601, 10814, 10804, 10854, 11082. Vendor Drake Dealer Hurlunk. Teleporter TO Invasion Point: Cataclysm +SDCategory: Shadowmoon Valley +EndScriptData */ + +/* ContentData +mob_mature_netherwing_drake +mob_enslaved_netherwing_drake +npc_drake_dealer_hurlunk +npc_invis_legion_teleporter +npcs_flanis_swiftwing_and_kagrosh +npc_murkblood_overseer +npc_neltharaku +npc_karynaku +npc_oronok_tornheart +EndContentData */ + +#include "precompiled.h" + +/*##### +# mob_mature_netherwing_drake +#####*/ + +#define SPELL_PLACE_CARCASS 38439 +#define SPELL_JUST_EATEN 38502 +#define SPELL_NETHER_BREATH 38467 + +#define SAY_JUST_EATEN "Thank you, mortal." + +struct MANGOS_DLL_DECL mob_mature_netherwing_drakeAI : public ScriptedAI +{ + mob_mature_netherwing_drakeAI(Creature* c) : ScriptedAI(c) + { + Reset(); + PlayerGUID = 0; + } + + uint64 PlayerGUID; + + bool IsEating; + bool Evade; + + uint32 ResetTimer; + uint32 CastTimer; + uint32 EatTimer; + + void Reset() + { + IsEating = false; + Evade = false; + + ResetTimer = 120000; + EatTimer = 5000; + CastTimer = 5000; + } + + void Aggro(Unit* who) { } + + void MoveInLineOfSight(Unit* who) + { + if(m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE) + return; + + ScriptedAI::MoveInLineOfSight(who); + } + + void SpellHit(Unit* caster, const SpellEntry* spell) + { + if(!caster) + return; + + if(caster->GetTypeId() == TYPEID_PLAYER && spell->Id == SPELL_PLACE_CARCASS && !m_creature->HasAura(SPELL_JUST_EATEN, 0) && !PlayerGUID) + { + float PlayerX, PlayerY, PlayerZ; + caster->GetClosePoint(PlayerX, PlayerY, PlayerZ, m_creature->GetObjectSize()); + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT + MOVEMENTFLAG_LEVITATING); + m_creature->GetMotionMaster()->MovePoint(1, PlayerX, PlayerY, PlayerZ); + PlayerGUID = caster->GetGUID(); + } + } + + void MovementInform(uint32 type, uint32 id) + { + if(type != POINT_MOTION_TYPE) + return; + + if(id == 1) + { + IsEating = true; + EatTimer = 5000; + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_ATTACKUNARMED); + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT + MOVEMENTFLAG_LEVITATING); + } + } + + void UpdateAI(const uint32 diff) + { + if(IsEating) + if(EatTimer < diff) + { + IsEating = false; + DoCast(m_creature, SPELL_JUST_EATEN); + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); + DoSay(SAY_JUST_EATEN, LANG_DRACONIC, NULL); + if(PlayerGUID) + { + Player* plr = ((Player*)Unit::GetUnit((*m_creature), PlayerGUID)); + if(plr && plr->GetQuestStatus(10804) == QUEST_STATUS_INCOMPLETE) + { + plr->KilledMonster(22131, m_creature->GetGUID()); + Evade = true; + PlayerGUID = 0; + } + } + }else EatTimer -= diff; + + if(Evade) + if(ResetTimer < diff) + EnterEvadeMode(); + else ResetTimer -= diff; + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(CastTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_NETHER_BREATH); + CastTimer = 5000; + }else CastTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_mature_netherwing_drake(Creature *_creature) +{ + return new mob_mature_netherwing_drakeAI(_creature); +} + +/*### +# mob_enslaved_netherwing_drake +####*/ + +#define FACTION_DEFAULT 62 +#define FACTION_FRIENDLY 1840 // Not sure if this is correct, it was taken off of Mordenai. + +#define SPELL_HIT_FORCE_OF_NELTHARAKU 38762 +#define SPELL_FORCE_OF_NELTHARAKU 38775 + +#define CREATURE_DRAGONMAW_SUBJUGATOR 21718 +#define CREATURE_ESCAPE_DUMMY 22317 + +struct MANGOS_DLL_DECL mob_enslaved_netherwing_drakeAI : public ScriptedAI +{ + mob_enslaved_netherwing_drakeAI(Creature* c) : ScriptedAI(c) + { + Reset(); + PlayerGUID = 0; + Tapped = false; + } + + uint64 PlayerGUID; + uint32 FlyTimer; + bool Tapped; + + void Reset() + { + if(!Tapped) + m_creature->setFaction(FACTION_DEFAULT); + + FlyTimer = 10000; + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT + MOVEMENTFLAG_LEVITATING); + m_creature->SetVisibility(VISIBILITY_ON); + } + + void Aggro(Unit* who) { } + + Creature* SelectCreatureInGrid(uint32 entry, float range) + { + Creature* pCreature = NULL; + + // Time for some omg mind blowing code to search for creature + CellPair pair(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY())); + Cell cell(pair); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck creature_check(*m_creature, entry, true, range); + MaNGOS::CreatureLastSearcher searcher(pCreature, creature_check); + + TypeContainerVisitor, GridTypeMapContainer> creature_searcher(searcher); + + CellLock cell_lock(cell, pair); + cell_lock->Visit(cell_lock, creature_searcher,*(m_creature->GetMap())); + + return pCreature; + } + + void SpellHit(Unit* caster, const SpellEntry* spell) + { + if(!caster) + return; + + if(caster->GetTypeId() == TYPEID_PLAYER && spell->Id == SPELL_HIT_FORCE_OF_NELTHARAKU && !Tapped) + { + Tapped = true; + PlayerGUID = caster->GetGUID(); + + m_creature->setFaction(FACTION_FRIENDLY); + DoCast(caster, SPELL_FORCE_OF_NELTHARAKU, true); + + Creature* Dragonmaw = SelectCreatureInGrid(CREATURE_DRAGONMAW_SUBJUGATOR, 50); + + if(Dragonmaw) + { + m_creature->AddThreat(Dragonmaw, 100000.0f); + AttackStart(Dragonmaw); + } + + HostilReference* ref = m_creature->getThreatManager().getOnlineContainer().getReferenceByTarget(caster); + if(ref) + ref->removeReference(); + } + } + + void MovementInform(uint32 type, uint32 id) + { + if(type != POINT_MOTION_TYPE) + return; + + if(id == 1) + { + if(PlayerGUID) + { + Unit* plr = Unit::GetUnit((*m_creature), PlayerGUID); + if(plr) + DoCast(plr, SPELL_FORCE_OF_NELTHARAKU, true); + + PlayerGUID = 0; + } + m_creature->SetVisibility(VISIBILITY_OFF); + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT + MOVEMENTFLAG_LEVITATING); + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + m_creature->RemoveCorpse(); + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + { + if(Tapped) + if(FlyTimer < diff) + { + Tapped = false; + if(PlayerGUID) + { + Player* plr = ((Player*)Unit::GetUnit((*m_creature), PlayerGUID)); + if(plr && plr->GetQuestStatus(10854) == QUEST_STATUS_INCOMPLETE) + { + plr->KilledMonster(22316, m_creature->GetGUID()); + /* + float x,y,z; + m_creature->GetPosition(x,y,z); + + float dx,dy,dz; + m_creature->GetRandomPoint(x, y, z, 20, dx, dy, dz); + dz += 20; // so it's in the air, not ground*/ + + float dx, dy, dz; + + Creature* EscapeDummy = SelectCreatureInGrid(CREATURE_ESCAPE_DUMMY, 30); + if(EscapeDummy) + EscapeDummy->GetPosition(dx, dy, dz); + else + { + m_creature->GetRandomPoint(m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 20, dx, dy, dz); + dz += 25; + } + + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT + MOVEMENTFLAG_LEVITATING); + m_creature->GetMotionMaster()->MovePoint(1, dx, dy, dz); + } + } + }else FlyTimer -= diff; + return; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_enslaved_netherwing_drake(Creature* _Creature) +{ + return new mob_enslaved_netherwing_drakeAI(_Creature); +} + +/*##### +# mob_dragonmaw_peon +#####*/ + +struct MANGOS_DLL_DECL mob_dragonmaw_peonAI : public ScriptedAI +{ + mob_dragonmaw_peonAI(Creature* c) : ScriptedAI(c) + { + Reset(); + } + + uint64 PlayerGUID; + bool Tapped; + uint32 PoisonTimer; + + void Reset() + { + PlayerGUID = 0; + Tapped = false; + PoisonTimer = 0; + } + + void Aggro(Unit* who) { } + + void SpellHit(Unit* caster, const SpellEntry* spell) + { + if(!caster) + return; + + if(caster->GetTypeId() == TYPEID_PLAYER && spell->Id == 40468 && !Tapped) + { + PlayerGUID = caster->GetGUID(); + + Tapped = true; + float x, y, z; + caster->GetClosePoint(x, y, z, m_creature->GetObjectSize()); + + m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + m_creature->GetMotionMaster()->MovePoint(1, x, y, z); + } + } + + void MovementInform(uint32 type, uint32 id) + { + if(type != POINT_MOTION_TYPE) + return; + + if(id) + { + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_EAT); + PoisonTimer = 15000; + } + } + + void UpdateAI(const uint32 diff) + { + if(PoisonTimer) + if(PoisonTimer <= diff) + { + if(PlayerGUID) + { + Player* plr = ((Player*)Unit::GetUnit((*m_creature), PlayerGUID)); + if(plr && plr->GetQuestStatus(11020) == QUEST_STATUS_INCOMPLETE) + plr->KilledMonster(23209, m_creature->GetGUID()); + } + PoisonTimer = 0; + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + }else PoisonTimer -= diff; + } +}; + +/*###### +## npc_drake_dealer_hurlunk +######*/ + +bool GossipHello_npc_drake_dealer_hurlunk(Player *player, Creature *_Creature) +{ + if (_Creature->isVendor() && player->GetReputationRank(1015) == REP_EXALTED) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_drake_dealer_hurlunk(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_TRADE) + player->SEND_VENDORLIST( _Creature->GetGUID() ); + + return true; +} + +/*###### +## npc_invis_legion_teleporter +######*/ + +#define SPELL_TELE_A_TO 37387 +#define SPELL_TELE_H_TO 37389 + +struct MANGOS_DLL_DECL npc_invis_legion_teleporterAI : public ScriptedAI +{ + npc_invis_legion_teleporterAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint64 PlayerGuid; + uint32 TeleTimer; + + void Reset() + { + PlayerGuid=0; + TeleTimer = 5000; + } + + void Aggro(Unit* who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || who->GetTypeId() != TYPEID_PLAYER) + return; + + if(who->GetTypeId() == TYPEID_PLAYER && m_creature->GetDistance(who)<4) + { + if (who->isAlive() || !who->isInCombat()) + PlayerGuid = who->GetGUID(); + } + } + + void UpdateAI(const uint32 diff) + { + if(TeleTimer < diff) + { + if(PlayerGuid) + { + Player* player = ((Player*)Unit::GetUnit((*m_creature), PlayerGuid)); + + if(m_creature->GetDistance(player)<3) + { + if(player->GetTeam()== ALLIANCE && player->GetQuestRewardStatus(10589)) + player->CastSpell(player,SPELL_TELE_A_TO,false); + if(player->GetTeam()== HORDE && player->GetQuestRewardStatus(10604)) + player->CastSpell(player,SPELL_TELE_H_TO,false); + } + PlayerGuid=0; + } + TeleTimer = 5000; + }else TeleTimer -= diff; + } +}; +CreatureAI* GetAI_npc_invis_legion_teleporter(Creature *_Creature) +{ + return new npc_invis_legion_teleporterAI (_Creature); +} + +/*###### +## npc_flanis_swiftwing_and_kagrosh +######*/ + +bool GossipHello_npcs_flanis_swiftwing_and_kagrosh(Player *player, Creature *_Creature) +{ + if (player->GetQuestStatus(10583) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(30658,1,true)) + player->ADD_GOSSIP_ITEM( 0, "Take Flanis's Pack", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + if (player->GetQuestStatus(10601) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(30659,1,true)) + player->ADD_GOSSIP_ITEM( 0, "Take Kagrosh's Pack", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npcs_flanis_swiftwing_and_kagrosh(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + ItemPosCountVec dest; + uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, 30658, 1, false); + if( msg == EQUIP_ERR_OK ) + { + player->StoreNewItem( dest, 30658, 1, true); + player->PlayerTalkClass->ClearMenus(); + } + } + if (action == GOSSIP_ACTION_INFO_DEF+2) + { + ItemPosCountVec dest; + uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, 30659, 1, false); + if( msg == EQUIP_ERR_OK ) + { + player->StoreNewItem( dest, 30659, 1, true); + player->PlayerTalkClass->ClearMenus(); + } + } + return true; +} + +/*###### +## npc_murkblood_overseer +######*/ + +#define QUEST_11082 11082 + +bool GossipHello_npc_murkblood_overseer(Player *player, Creature *_Creature) +{ + if (player->GetQuestStatus(QUEST_11082) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "I am here for you, overseer.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(10940, _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_murkblood_overseer(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM(0, "How dare you question an overseer of the Dragonmaw!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + //correct id not known + player->SEND_GOSSIP_MENU(10940, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM(0, "Who speaks of me? What are you talking about, broken?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + //correct id not known + player->SEND_GOSSIP_MENU(10940, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM(0, "Continue please.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + //correct id not known + player->SEND_GOSSIP_MENU(10940, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM(0, "Who are these bidders?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + //correct id not known + player->SEND_GOSSIP_MENU(10940, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->ADD_GOSSIP_ITEM(0, "Well... yes.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+6); + //correct id not known + player->SEND_GOSSIP_MENU(10940, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + //correct id not known + player->SEND_GOSSIP_MENU(10940, _Creature->GetGUID()); + _Creature->CastSpell(player,41121,false); + player->AreaExploredOrEventHappens(QUEST_11082); + break; + } + return true; +} + +/*###### +## npc_neltharaku +######*/ + +bool GossipHello_npc_neltharaku(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(10814) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "I am listening, dragon", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(10613, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_neltharaku(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "But you are dragons! How could orcs do this to you?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(10614, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "Your mate?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->SEND_GOSSIP_MENU(10615, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, "I have battled many beasts, dragon. I will help you.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + player->SEND_GOSSIP_MENU(10616, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(10814); + break; + } + return true; +} + +/*###### +## npc_oronok +######*/ + +#define GOSSIP_ORONOK1 "I am ready to hear your story, Oronok." +#define GOSSIP_ORONOK2 "How do I find the cipher?" +#define GOSSIP_ORONOK3 "How do you know all of this?" +#define GOSSIP_ORONOK4 "Yet what? What is it, Oronok?" +#define GOSSIP_ORONOK5 "Continue, please." +#define GOSSIP_ORONOK6 "So what of the cipher now? And your boys?" +#define GOSSIP_ORONOK7 "I will find your boys and the cipher, Oronok." + +bool GossipHello_npc_oronok_tornheart(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + if (_Creature->isVendor()) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + + if (player->GetQuestStatus(10519) == QUEST_STATUS_INCOMPLETE) + { + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ORONOK1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(10312, _Creature->GetGUID()); + }else + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_oronok_tornheart(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_TRADE: + player->SEND_VENDORLIST( _Creature->GetGUID() ); + break; + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ORONOK2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(10313, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ORONOK3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(10314, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ORONOK4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->SEND_GOSSIP_MENU(10315, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ORONOK5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + player->SEND_GOSSIP_MENU(10316, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ORONOK6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(10317, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ORONOK7, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+6); + player->SEND_GOSSIP_MENU(10318, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(10519); + break; + } + return true; +} + +/*#### +# npc_karynaku +####*/ + +bool QuestAccept_npc_karynaku(Player* player, Creature* creature, Quest const* quest) +{ + if(quest->GetQuestId() == 10870) // Ally of the Netherwing + { + std::vector nodes; + + nodes.resize(2); + nodes[0] = 161; // From Karynaku + nodes[1] = 162; // To Mordenai + error_log("SD2: Player %s started quest 10870 which has disabled taxi node, need to be fixed in core", player->GetName()); + //player->ActivateTaxiPathTo(nodes, 20811); + } + + return true; +} + +void AddSC_shadowmoon_valley() +{ + Script *newscript; + + newscript = new Script; + newscript->Name = "mob_mature_netherwing_drake"; + newscript->GetAI = GetAI_mob_mature_netherwing_drake; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "mob_enslaved_netherwing_drake"; + newscript->GetAI = GetAI_mob_enslaved_netherwing_drake; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_drake_dealer_hurlunk"; + newscript->pGossipHello = &GossipHello_npc_drake_dealer_hurlunk; + newscript->pGossipSelect = &GossipSelect_npc_drake_dealer_hurlunk; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_invis_legion_teleporter"; + newscript->GetAI = GetAI_npc_invis_legion_teleporter; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npcs_flanis_swiftwing_and_kagrosh"; + newscript->pGossipHello = &GossipHello_npcs_flanis_swiftwing_and_kagrosh; + newscript->pGossipSelect = &GossipSelect_npcs_flanis_swiftwing_and_kagrosh; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_murkblood_overseer"; + newscript->pGossipHello = &GossipHello_npc_murkblood_overseer; + newscript->pGossipSelect = &GossipSelect_npc_murkblood_overseer; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_neltharaku"; + newscript->pGossipHello = &GossipHello_npc_neltharaku; + newscript->pGossipSelect = &GossipSelect_npc_neltharaku; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "npc_karynaku"; + newscript->pQuestAccept = &QuestAccept_npc_karynaku; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_oronok_tornheart"; + newscript->pGossipHello = &GossipHello_npc_oronok_tornheart; + newscript->pGossipSelect = &GossipSelect_npc_oronok_tornheart; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/shattrath/shattrath_city.cpp b/src/bindings/scripts/scripts/zone/shattrath/shattrath_city.cpp new file mode 100644 index 00000000000..8ad5a6e6321 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/shattrath/shattrath_city.cpp @@ -0,0 +1,268 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Shattrath_City +SD%Complete: 100 +SDComment: Quest support: 10004, 10009. Flask vendors, Teleport to Caverns of Time +SDCategory: Shattrath City +EndScriptData */ + +/* ContentData +npc_raliq_the_drunk +npc_salsalabim +npc_shattrathflaskvendors +npc_zephyr +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_raliq_the_drunk +######*/ + +#define GOSSIP_RALIQ "You owe Sim'salabim money. Hand them over or die!" + +#define FACTION_HOSTILE_RD 45 +#define FACTION_FRIENDLY_RD 35 + +#define SPELL_UPPERCUT 10966 + +struct MANGOS_DLL_DECL npc_raliq_the_drunkAI : public ScriptedAI +{ + npc_raliq_the_drunkAI(Creature* c) : ScriptedAI(c) { Reset(); } + + uint32 Uppercut_Timer; + + void Reset() + { + Uppercut_Timer = 5000; + m_creature->setFaction(FACTION_FRIENDLY_RD); + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( Uppercut_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_UPPERCUT); + Uppercut_Timer = 15000; + }else Uppercut_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_raliq_the_drunk(Creature *_Creature) +{ + return new npc_raliq_the_drunkAI (_Creature); +} + +bool GossipHello_npc_raliq_the_drunk(Player *player, Creature *_Creature ) +{ + if( player->GetQuestStatus(10009) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM(1, GOSSIP_RALIQ, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(9440, _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_raliq_the_drunk(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_ACTION_INFO_DEF+1 ) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->setFaction(FACTION_HOSTILE_RD); + ((npc_raliq_the_drunkAI*)_Creature->AI())->AttackStart(player); + } + return true; +} + +/*###### +# npc_salsalabim +######*/ + +#define FACTION_HOSTILE_SA 90 +#define FACTION_FRIENDLY_SA 35 +#define QUEST_10004 10004 + +#define SPELL_MAGNETIC_PULL 31705 + +struct MANGOS_DLL_DECL npc_salsalabimAI : public ScriptedAI +{ + npc_salsalabimAI(Creature* c) : ScriptedAI(c) { Reset(); } + + uint32 MagneticPull_Timer; + + void Reset() + { + MagneticPull_Timer = 15000; + m_creature->setFaction(FACTION_FRIENDLY_SA); + } + + void Aggro(Unit *who) {} + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if( done_by->GetTypeId() == TYPEID_PLAYER ) + if( (m_creature->GetHealth()-damage)*100 / m_creature->GetMaxHealth() < 20 ) + { + ((Player*)done_by)->GroupEventHappens(QUEST_10004,m_creature); + damage = 0; + EnterEvadeMode(); + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( MagneticPull_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_MAGNETIC_PULL); + MagneticPull_Timer = 15000; + }else MagneticPull_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_salsalabim(Creature *_Creature) +{ + return new npc_salsalabimAI (_Creature); +} + +bool GossipHello_npc_salsalabim(Player *player, Creature *_Creature) +{ + if( player->GetQuestStatus(QUEST_10004) == QUEST_STATUS_INCOMPLETE ) + { + _Creature->setFaction(FACTION_HOSTILE_SA); + ((npc_salsalabimAI*)_Creature->AI())->AttackStart(player); + } + else + { + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + } + return true; +} + +/* +################################################## +Shattrath City Flask Vendors provides flasks to people exalted with 3 factions: +Haldor the Compulsive +Arcanist Xorith +Both sell special flasks for use in Outlands 25man raids only, +purchasable for one Mark of Illidari each +Purchase requires exalted reputation with Scryers/Aldor, Cenarion Expedition and The Sha'tar +################################################## +*/ + +bool GossipHello_npc_shattrathflaskvendors(Player *player, Creature *_Creature) +{ + if(_Creature->GetEntry() == 23484) + { + // Aldor vendor + if( _Creature->isVendor() && (player->GetReputationRank(932) == REP_EXALTED) && (player->GetReputationRank(935) == REP_EXALTED) && (player->GetReputationRank(942) == REP_EXALTED) ) + { + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + player->SEND_GOSSIP_MENU(11085, _Creature->GetGUID()); + } + else + { + player->SEND_GOSSIP_MENU(11083, _Creature->GetGUID()); + } + } + + if(_Creature->GetEntry() == 23483) + { + // Scryers vendor + if( _Creature->isVendor() && (player->GetReputationRank(934) == REP_EXALTED) && (player->GetReputationRank(935) == REP_EXALTED) && (player->GetReputationRank(942) == REP_EXALTED) ) + { + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + player->SEND_GOSSIP_MENU(11085, _Creature->GetGUID()); + } + else + { + player->SEND_GOSSIP_MENU(11084, _Creature->GetGUID()); + } + } + + return true; +} + +bool GossipSelect_npc_shattrathflaskvendors(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if( action == GOSSIP_ACTION_TRADE ) + player->SEND_VENDORLIST( _Creature->GetGUID() ); + + return true; +} + +/*###### +# npc_zephyr +######*/ + +bool GossipHello_npc_zephyr(Player *player, Creature *_Creature) +{ + if( player->GetReputationRank(989) >= REP_REVERED ) + player->ADD_GOSSIP_ITEM(0, "Take me to the Caverns of Time.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_zephyr(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if( action == GOSSIP_ACTION_INFO_DEF+1 ) + player->CastSpell(player,37778,false); + + return true; +} + +void AddSC_shattrath_city() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_raliq_the_drunk"; + newscript->pGossipHello = &GossipHello_npc_raliq_the_drunk; + newscript->pGossipSelect = &GossipSelect_npc_raliq_the_drunk; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_salsalabim"; + newscript->GetAI = GetAI_npc_salsalabim; + newscript->pGossipHello = &GossipHello_npc_salsalabim; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_shattrathflaskvendors"; + newscript->pGossipHello = &GossipHello_npc_shattrathflaskvendors; + newscript->pGossipSelect = &GossipSelect_npc_shattrathflaskvendors; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_zephyr"; + newscript->pGossipHello = &GossipHello_npc_zephyr; + newscript->pGossipSelect = &GossipSelect_npc_zephyr; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/silithus/silithus.cpp b/src/bindings/scripts/scripts/zone/silithus/silithus.cpp new file mode 100644 index 00000000000..8b7681d0557 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/silithus/silithus.cpp @@ -0,0 +1,149 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Silithus +SD%Complete: 100 +SDComment: Quest support: 8304. +SDCategory: Silithus +EndScriptData */ + +/* ContentData +npcs_rutgar_and_frankal +EndContentData */ + +#include "precompiled.h" + +/*### +## npcs_rutgar_and_frankal +###*/ + +//gossip item text best guess +#define GOSSIP_ITEM1 "I seek information about Natalia" + +#define GOSSIP_ITEM2 "That sounds dangerous!" +#define GOSSIP_ITEM3 "What did you do?" +#define GOSSIP_ITEM4 "Who?" +#define GOSSIP_ITEM5 "Women do that. What did she demand?" +#define GOSSIP_ITEM6 "What do you mean?" +#define GOSSIP_ITEM7 "What happened next?" + +#define GOSSIP_ITEM11 "Yes, please continue" +#define GOSSIP_ITEM12 "What language?" +#define GOSSIP_ITEM13 "The Priestess attacked you?!" +#define GOSSIP_ITEM14 "I should ask the monkey about this" +#define GOSSIP_ITEM15 "Then what..." + +//trigger creatures to kill +#define TRIGGER_RUTGAR 15222 +#define TRIGGER_FRANKAL 15221 + +bool GossipHello_npcs_rutgar_and_frankal(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(8304) == QUEST_STATUS_INCOMPLETE && + _Creature->GetEntry() == 15170 && + !player->GetReqKillOrCastCurrentCount(8304, TRIGGER_RUTGAR )) + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + if (player->GetQuestStatus(8304) == QUEST_STATUS_INCOMPLETE && + _Creature->GetEntry() == 15171 && + player->GetReqKillOrCastCurrentCount(8304, TRIGGER_RUTGAR )) + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+9); + + player->SEND_GOSSIP_MENU(7754, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npcs_rutgar_and_frankal(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(7755, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 1: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(7756, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(7757, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 3: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->SEND_GOSSIP_MENU(7758, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 4: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(7759, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 5: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM7, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(7760, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 6: + player->SEND_GOSSIP_MENU(7761, _Creature->GetGUID()); + //'kill' our trigger to update quest status + player->KilledMonster( TRIGGER_RUTGAR, _Creature->GetGUID() ); + break; + + case GOSSIP_ACTION_INFO_DEF + 9: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM11, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(7762, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 10: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM12, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->SEND_GOSSIP_MENU(7763, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 11: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM13, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->SEND_GOSSIP_MENU(7764, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 12: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM14, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13); + player->SEND_GOSSIP_MENU(7765, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 13: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM15, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14); + player->SEND_GOSSIP_MENU(7766, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 14: + player->SEND_GOSSIP_MENU(7767, _Creature->GetGUID()); + //'kill' our trigger to update quest status + player->KilledMonster( TRIGGER_FRANKAL, _Creature->GetGUID() ); + break; + } + return true; +} + +/*### +## +####*/ + +void AddSC_silithus() +{ + Script *newscript; + newscript = new Script; + newscript->Name="npcs_rutgar_and_frankal"; + newscript->pGossipHello = &GossipHello_npcs_rutgar_and_frankal; + newscript->pGossipSelect = &GossipSelect_npcs_rutgar_and_frankal; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/silvermoon/silvermoon_city.cpp b/src/bindings/scripts/scripts/zone/silvermoon/silvermoon_city.cpp new file mode 100644 index 00000000000..1a237eefde0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/silvermoon/silvermoon_city.cpp @@ -0,0 +1,103 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Silvermoon_City +SD%Complete: 100 +SDComment: Quest support: 9685 +SDCategory: Silvermoon City +EndScriptData */ + +/* ContentData +npc_blood_knight_stillblade +EndContentData */ + +#include "precompiled.h" + +/*####### +# npc_blood_knight_stillblade +#######*/ + +#define SAY_HEAL "Thank you, dear Paladin, you just saved my life." + +#define QUEST_REDEEMING_THE_DEAD 9685 +#define SPELL_SHIMMERING_VESSEL 31225 +#define SPELL_REVIVE_SELF 32343 + +struct MANGOS_DLL_DECL npc_blood_knight_stillbladeAI : public ScriptedAI +{ + npc_blood_knight_stillbladeAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 lifeTimer; + bool spellHit; + + void Reset() + { + lifeTimer = 120000; + m_creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 32); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,7); // lay down + spellHit = false; + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + return; + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->GetUInt32Value(UNIT_FIELD_BYTES_1)) + { + if(lifeTimer < diff) + m_creature->AI()->EnterEvadeMode(); + else + lifeTimer -= diff; + } + } + + void SpellHit(Unit *Hitter, const SpellEntry *Spellkind) + { + if((Spellkind->Id == SPELL_SHIMMERING_VESSEL) && !spellHit && + (Hitter->GetTypeId() == TYPEID_PLAYER) && (((Player*)Hitter)->IsActiveQuest(QUEST_REDEEMING_THE_DEAD))) + { + ((Player*)Hitter)->AreaExploredOrEventHappens(QUEST_REDEEMING_THE_DEAD); + DoCast(m_creature,SPELL_REVIVE_SELF); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,0); + m_creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, 0); + //m_creature->RemoveAllAuras(); + DoSay(SAY_HEAL,LANG_COMMON,NULL); + spellHit = true; + } + } +}; + +CreatureAI* GetAI_npc_blood_knight_stillblade(Creature *_Creature) +{ + return new npc_blood_knight_stillbladeAI (_Creature); +} + +void AddSC_silvermoon_city() +{ + Script *newscript; + newscript = new Script; + newscript->Name="npc_blood_knight_stillblade"; + newscript->GetAI = GetAI_npc_blood_knight_stillblade; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/silverpine_forest/silverpine_forest.cpp b/src/bindings/scripts/scripts/zone/silverpine_forest/silverpine_forest.cpp new file mode 100644 index 00000000000..87c346d58b4 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/silverpine_forest/silverpine_forest.cpp @@ -0,0 +1,100 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Silverpine_Forest +SD%Complete: 100 +SDComment: Quest support: 1886 +SDCategory: Silverpine Forest +EndScriptData */ + +/* ContentData +npc_astor_hadren +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_astor_hadren +######*/ + +struct MANGOS_DLL_DECL npc_astor_hadrenAI : public ScriptedAI +{ + npc_astor_hadrenAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + m_creature->setFaction(68); + } + + void Aggro(Unit* who) + { + } + + void JustDied(Unit *who) + { + m_creature->setFaction(68); + } +}; + +CreatureAI* GetAI_npc_astor_hadren(Creature *_creature) +{ + return new npc_astor_hadrenAI(_creature); +} + +bool GossipHello_npc_astor_hadren(Player *player, Creature *_Creature) +{ + if (player->GetQuestStatus(1886) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "You're Astor Hadren, right?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + + player->SEND_GOSSIP_MENU(623, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_astor_hadren(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF + 1: + player->ADD_GOSSIP_ITEM( 0, "You've got something I need, Astor. And I'll be taking it now.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(624, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + player->CLOSE_GOSSIP_MENU(); + _Creature->setFaction(21); + if(player) + ((npc_astor_hadrenAI*)_Creature->AI())->AttackStart(player); + break; + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_silverpine_forest() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_astor_hadren"; + newscript->pGossipHello = &GossipHello_npc_astor_hadren; + newscript->pGossipSelect = &GossipSelect_npc_astor_hadren; + newscript->GetAI = GetAI_npc_astor_hadren; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stonetalon_mountains/stonetalon_mountains.cpp b/src/bindings/scripts/scripts/zone/stonetalon_mountains/stonetalon_mountains.cpp new file mode 100644 index 00000000000..652af2fe802 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stonetalon_mountains/stonetalon_mountains.cpp @@ -0,0 +1,80 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Stonetalon_Mountains +SD%Complete: 95 +SDComment: Quest support: 6627 (Braug Dimspirits questions/'answers' might have more to it, need more info) +SDCategory: Stonetalon Mountains +EndScriptData */ + +#include "precompiled.h" + +/*###### +## npc_braug_dimspirit +######*/ + +bool GossipHello_npc_braug_dimspirit(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(6627) == QUEST_STATUS_INCOMPLETE) + { + player->ADD_GOSSIP_ITEM( 0, "Ysera", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->ADD_GOSSIP_ITEM( 0, "Neltharion", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->ADD_GOSSIP_ITEM( 0, "Nozdormu", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->ADD_GOSSIP_ITEM( 0, "Alexstrasza", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->ADD_GOSSIP_ITEM( 0, "Malygos", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(5820, _Creature->GetGUID()); + } + else + player->SEND_GOSSIP_MENU(5819, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_braug_dimspirit(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->CastSpell(player,6766,false); + + } + if (action == GOSSIP_ACTION_INFO_DEF+2) + { + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(6627); + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_stonetalon_mountains() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_braug_dimspirit"; + newscript->pGossipHello = &GossipHello_npc_braug_dimspirit; + newscript->pGossipSelect = &GossipSelect_npc_braug_dimspirit; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stormwind/stormwind_city.cpp b/src/bindings/scripts/scripts/zone/stormwind/stormwind_city.cpp new file mode 100644 index 00000000000..0821a424cc4 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stormwind/stormwind_city.cpp @@ -0,0 +1,272 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Stormwind_City +SD%Complete: 100 +SDComment: Quest support: 1640, 1447, 4185, 11223 (DB support required for spell 42711). Receive emote General Marcus +SDCategory: Stormwind City +EndScriptData */ + +/* ContentData +npc_archmage_malin +npc_bartleby +npc_dashel_stonefist +npc_general_marcus_jonathan +npc_lady_katrana_prestor +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_archmage_malin +######*/ + +#define GOSSIP_ITEM_MALIN "Can you send me to Theramore? I have an urgent message for Lady Jaina from Highlord Bolvar." + +bool GossipHello_npc_archmage_malin(Player *player, Creature *_Creature) +{ + if(_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if(player->GetQuestStatus(11223) == QUEST_STATUS_COMPLETE && !player->GetQuestRewardStatus(11223)) + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM_MALIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_archmage_malin(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if(action = GOSSIP_ACTION_INFO_DEF) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->CastSpell(player, 42711, true); + } + + return true; +} + +/*###### +## npc_bartleby +######*/ + +struct MANGOS_DLL_DECL npc_bartlebyAI : public ScriptedAI +{ + npc_bartlebyAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint64 PlayerGUID; + + void Reset() + { + m_creature->setFaction(11); + m_creature->setEmoteState(7); + + PlayerGUID = 0; + } + + void JustDied(Unit *who) + { + m_creature->setFaction(11); + } + + void DamageTaken(Unit *done_by, uint32 & damage) + { + if(damage > m_creature->GetHealth() || ((m_creature->GetHealth() - damage)*100 / m_creature->GetMaxHealth() < 15)) + { + //Take 0 damage + damage = 0; + + if (done_by->GetTypeId() == TYPEID_PLAYER && done_by->GetGUID() == PlayerGUID) + { + ((Player*)done_by)->AttackStop(); + ((Player*)done_by)->AreaExploredOrEventHappens(1640); + } + m_creature->CombatStop(); + EnterEvadeMode(); + } + } + + void Aggro(Unit *who) {} +}; + +bool QuestAccept_npc_bartleby(Player *player, Creature *_Creature, Quest const *_Quest) +{ + if(_Quest->GetQuestId() == 1640) + { + _Creature->setFaction(168); + ((npc_bartlebyAI*)_Creature->AI())->PlayerGUID = player->GetGUID(); + ((npc_bartlebyAI*)_Creature->AI())->AttackStart(player); + } + return true; +} + +CreatureAI* GetAI_npc_bartleby(Creature *_creature) +{ + return new npc_bartlebyAI(_creature); +} + +/*###### +## npc_dashel_stonefist +######*/ + +struct MANGOS_DLL_DECL npc_dashel_stonefistAI : public ScriptedAI +{ + npc_dashel_stonefistAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + m_creature->setFaction(11); + m_creature->setEmoteState(7); + } + + void DamageTaken(Unit *done_by, uint32 & damage) + { + if((damage > m_creature->GetHealth()) || (m_creature->GetHealth() - damage)*100 / m_creature->GetMaxHealth() < 15) + { + //Take 0 damage + damage = 0; + + if (done_by->GetTypeId() == TYPEID_PLAYER) + { + ((Player*)done_by)->AttackStop(); + ((Player*)done_by)->AreaExploredOrEventHappens(1447); + } + //m_creature->CombatStop(); + EnterEvadeMode(); + } + AttackedBy(done_by); + } + + void Aggro(Unit *who) {} +}; + +bool QuestAccept_npc_dashel_stonefist(Player *player, Creature *_Creature, Quest const *_Quest) +{ + if(_Quest->GetQuestId() == 1447) + { + _Creature->setFaction(168); + ((npc_dashel_stonefistAI*)_Creature->AI())->AttackStart(player); + } + return true; +} + +CreatureAI* GetAI_npc_dashel_stonefist(Creature *_creature) +{ + return new npc_dashel_stonefistAI(_creature); +} + +/*###### +## npc_general_marcus_jonathan +######*/ + +bool ReceiveEmote_npc_general_marcus_jonathan(Player *player, Creature *_Creature, uint32 emote) +{ + if(player->GetTeam() == ALLIANCE) + { + if (emote == TEXTEMOTE_SALUTE) + { + _Creature->SetOrientation(_Creature->GetAngle(player)); + _Creature->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); + } + if (emote == TEXTEMOTE_WAVE) + { + _Creature->MonsterSay("Greetings citizen",LANG_COMMON,0); + } + } + return true; +} + +/*###### +## npc_lady_katrana_prestor +######*/ + +#define GOSSIP_ITEM_KAT_1 "Pardon the intrusion, Lady Prestor, but Highlord Bolvar suggested that I seek your advice." +#define GOSSIP_ITEM_KAT_2 "My apologies, Lady Prestor." +#define GOSSIP_ITEM_KAT_3 "Begging your pardon, Lady Prestor. That was not my intent." +#define GOSSIP_ITEM_KAT_4 "Thank you for your time, Lady Prestor." + +bool GossipHello_npc_lady_katrana_prestor(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(4185) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_KAT_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(2693, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_lady_katrana_prestor(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_KAT_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(2694, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_KAT_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(2695, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_KAT_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(2696, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(4185); + break; + } + return true; +} + +void AddSC_stormwind_city() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_archmage_malin"; + newscript->pGossipHello = &GossipHello_npc_archmage_malin; + newscript->pGossipSelect = &GossipSelect_npc_archmage_malin; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "npc_bartleby"; + newscript->GetAI = GetAI_npc_bartleby; + newscript->pQuestAccept = &QuestAccept_npc_bartleby; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "npc_dashel_stonefist"; + newscript->GetAI = GetAI_npc_dashel_stonefist; + newscript->pQuestAccept = &QuestAccept_npc_dashel_stonefist; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name = "npc_general_marcus_jonathan"; + newscript->pReceiveEmote = &ReceiveEmote_npc_general_marcus_jonathan; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_lady_katrana_prestor"; + newscript->pGossipHello = &GossipHello_npc_lady_katrana_prestor; + newscript->pGossipSelect = &GossipSelect_npc_lady_katrana_prestor; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stranglethorn_vale/stranglethorn_vale.cpp b/src/bindings/scripts/scripts/zone/stranglethorn_vale/stranglethorn_vale.cpp new file mode 100644 index 00000000000..35fda2c6f1c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stranglethorn_vale/stranglethorn_vale.cpp @@ -0,0 +1,107 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Stranglethorn_Vale +SD%Complete: 100 +SDComment: Quest support: 592 +SDCategory: Stranglethorn Vale +EndScriptData */ + +/* ContentData +mob_yenniku +EndContentData */ + +#include "precompiled.h" + +/*###### +## mob_yenniku +######*/ + +struct MANGOS_DLL_DECL mob_yennikuAI : public ScriptedAI +{ + mob_yennikuAI(Creature *c) : ScriptedAI(c) + { + bReset = false; + Reset(); + } + + uint32 Reset_Timer; + bool bReset; + + void Reset() + { + Reset_Timer = 0; + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); + } + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + if (caster->GetTypeId() == TYPEID_PLAYER) + { + //Yenniku's Release + if(!bReset && ((Player*)caster)->GetQuestStatus(592) == QUEST_STATUS_INCOMPLETE && spell->Id == 3607) + { + m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN); + m_creature->CombatStop(); //stop combat + m_creature->DeleteThreatList(); //unsure of this + m_creature->setFaction(83); //horde generic + + bReset = true; + Reset_Timer = 60000; + } + } + return; + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if (bReset) + if(Reset_Timer < diff) + { + EnterEvadeMode(); + bReset = false; + m_creature->setFaction(28); //troll, bloodscalp + } + else Reset_Timer -= diff; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_mob_yenniku(Creature *_Creature) +{ + return new mob_yennikuAI (_Creature); +} + +/*###### +## +######*/ + +void AddSC_stranglethorn_vale() +{ + Script *newscript; + + newscript = new Script; + newscript->Name = "mob_yenniku"; + newscript->GetAI = GetAI_mob_yenniku; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_baron_rivendare.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_baron_rivendare.cpp new file mode 100644 index 00000000000..6355588e7b8 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_baron_rivendare.cpp @@ -0,0 +1,215 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: boss_baron_rivendare +SD%Complete: 100 +SDComment: aura applied/defined in database +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +#define SAY_0 "Intruders! More pawns of the Argent Dawn, no doubt. I already count one of their number among my prisoners. Withdraw from my domain before she is executed!" +#define SAY_1 "You're still here? Your foolishness is amusing! The Argent Dawn wench needn't suffer in vain. Leave at once and she shall be spared!" +#define SAY_2 "I shall take great pleasure in taking this poor wretch's life! It's not too late, she needn't suffer in vain. Turn back and her death shall be merciful!" +#define SAY_3 "May this prisoner's death serve as a warning. None shall defy the Scourge and live!" +#define SAY_4 "So you see fit to toy with the Lich King's creations? Ramstein, be sure to give the intruders a proper greeting." +#define SAY_5 "Time to take matters into my own hands. Come. Enter my domain and challenge the might of the Scourge!" + +#define ADD_1X 4017.403809 +#define ADD_1Y -3339.703369 +#define ADD_1Z 115.057655 +#define ADD_1O 5.487860 + +#define ADD_2X 4013.189209 +#define ADD_2Y -3351.808350 +#define ADD_2Z 115.052254 +#define ADD_2O 0.134280 + +#define ADD_3X 4017.738037 +#define ADD_3Y -3363.478016 +#define ADD_3Z 115.057274 +#define ADD_3O 0.723313 + +#define ADD_4X 4048.877197 +#define ADD_4Y -3363.223633 +#define ADD_4Z 115.054253 +#define ADD_4O 3.627735 + +#define ADD_5X 4051.777588 +#define ADD_5Y -3350.893311 +#define ADD_5Z 115.055351 +#define ADD_5O 3.066176 + +#define ADD_6X 4048.375977 +#define ADD_6Y -3339.966309 +#define ADD_6Z 115.055222 +#define ADD_6O 2.457497 + +#define SPELL_SHADOWBOLT 18164 +#define SPELL_CLEAVE 15584 +#define SPELL_MORTALSTRIKE 13737 + +// spell 17473 should trigger -> 17471 + +//#define SPELL_RAISEDEAD 17475 +//#define SPELL_DEATHPACT 17698 +//#define SPELL_SUMMONSKELETONS 17274 + +struct MANGOS_DLL_DECL boss_baron_rivendareAI : public ScriptedAI +{ + boss_baron_rivendareAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowBolt_Timer; + uint32 Cleave_Timer; + uint32 MortalStrike_Timer; + // uint32 RaiseDead_Timer; + uint32 SummonSkeletons_Timer; + Creature *Summoned; + + void Reset() + { + ShadowBolt_Timer = 5000; + Cleave_Timer = 8000; + MortalStrike_Timer = 12000; + // RaiseDead_Timer = 30000; + SummonSkeletons_Timer = 34000; + + m_creature->LoadCreaturesAddon(); + } + + void Aggro(Unit *who) + { + switch (rand()%6) + { + case 0: + DoYell(SAY_0,LANG_UNIVERSAL,NULL); + break; + case 1: + DoYell(SAY_1,LANG_UNIVERSAL,NULL); + break; + case 2: + DoYell(SAY_2,LANG_UNIVERSAL,NULL); + break; + case 3: + DoYell(SAY_3,LANG_UNIVERSAL,NULL); + break; + case 4: + DoYell(SAY_4,LANG_UNIVERSAL,NULL); + break; + case 5: + DoYell(SAY_5,LANG_UNIVERSAL,NULL); + break; + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //ShadowBolt + if (ShadowBolt_Timer < diff) + { + //Cast + if (rand()%100 < 70) //70% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_SHADOWBOLT); + } + //10 seconds until we should cast this again + ShadowBolt_Timer = 10000; + }else ShadowBolt_Timer -= diff; + + //Cleave + if (Cleave_Timer < diff) + { + //Cast + if (rand()%100 < 55) //55% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + } + //13 seconds until we should cast this again + Cleave_Timer = 12000; + }else Cleave_Timer -= diff; + + //MortalStrike + if (MortalStrike_Timer < diff) + { + //Cast + if (rand()%100 < 30) //30% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_MORTALSTRIKE); + } + //16 seconds until we should cast this again + MortalStrike_Timer = 16000; + }else MortalStrike_Timer -= diff; + + //RaiseDead + // if (RaiseDead_Timer < diff) + // { + //Cast + // DoCast(m_creature,SPELL_RAISEDEAD); + // DoSay("summon triggered",LANG_UNIVERSAL,NULL); //just a checkpoint + //45 seconds until we should cast this again + // RaiseDead_Timer = 45000; + // }else RaiseDead_Timer -= diff; + + //SummonSkeletons + //Creature* Unit::SummonCreature(uint32 id, float x, float y, float z, float ang,TempSummonType spwtype,uint32 despwtime); + + if (SummonSkeletons_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + //Cast + Summoned = m_creature->SummonCreature(11197,ADD_1X,ADD_1Y,ADD_1Z,ADD_1O,TEMPSUMMON_TIMED_DESPAWN,29000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(11197,ADD_2X,ADD_2Y,ADD_2Z,ADD_2O,TEMPSUMMON_TIMED_DESPAWN,29000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(11197,ADD_3X,ADD_3Y,ADD_3Z,ADD_3O,TEMPSUMMON_TIMED_DESPAWN,29000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(11197,ADD_4X,ADD_4Y,ADD_4Z,ADD_4O,TEMPSUMMON_TIMED_DESPAWN,29000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(11197,ADD_5X,ADD_5Y,ADD_5Z,ADD_5O,TEMPSUMMON_TIMED_DESPAWN,29000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + Summoned = m_creature->SummonCreature(11197,ADD_6X,ADD_6Y,ADD_6Z,ADD_6O,TEMPSUMMON_TIMED_DESPAWN,29000); + ((CreatureAI*)Summoned->AI())->AttackStart(target); + + //34 seconds until we should cast this again + SummonSkeletons_Timer = 40000; + }else SummonSkeletons_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_baron_rivendare(Creature *_Creature) +{ + return new boss_baron_rivendareAI (_Creature); +} + + +void AddSC_boss_baron_rivendare() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_baron_rivendare"; + newscript->GetAI = GetAI_boss_baron_rivendare; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_baroness_anastari.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_baroness_anastari.cpp new file mode 100644 index 00000000000..79284eb5a26 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_baroness_anastari.cpp @@ -0,0 +1,124 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: boss_baroness_anastari +SD%Complete: 90 +SDComment: MC disabled +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_BANSHEEWAIL 16565 +#define SPELL_BANSHEECURSE 16867 +#define SPELL_SILENCE 18327 +//#define SPELL_POSSESS 17244 + +struct MANGOS_DLL_DECL boss_baroness_anastariAI : public ScriptedAI +{ + boss_baroness_anastariAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 BansheeWail_Timer; + uint32 BansheeCurse_Timer; + uint32 Silence_Timer; + //uint32 Possess_Timer; + + void Reset() + { + BansheeWail_Timer = 1000; + BansheeCurse_Timer = 11000; + Silence_Timer = 13000; + //Possess_Timer = 35000; + } + + void Aggro(Unit *who) + { + } + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //BansheeWail + if (BansheeWail_Timer < diff) + { + //Cast + if (rand()%100 < 95) //95% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_BANSHEEWAIL); + } + //4 seconds until we should cast this again + BansheeWail_Timer = 4000; + }else BansheeWail_Timer -= diff; + + //BansheeCurse + if (BansheeCurse_Timer < diff) + { + //Cast + if (rand()%100 < 75) //75% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_BANSHEECURSE); + } + //18 seconds until we should cast this again + BansheeCurse_Timer = 18000; + }else BansheeCurse_Timer -= diff; + + //Silence + if (Silence_Timer < diff) + { + //Cast + if (rand()%100 < 80) //80% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_SILENCE); + } + //13 seconds until we should cast this again + Silence_Timer = 13000; + }else Silence_Timer -= diff; + + //Possess + /* if (Possess_Timer < diff) + { + //Cast + if (rand()%100 < 65) //65% chance to cast + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target)DoCast(target,SPELL_POSSESS); + } + //50 seconds until we should cast this again + Possess_Timer = 50000; + }else Possess_Timer -= diff; + */ + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_baroness_anastari(Creature *_Creature) +{ + return new boss_baroness_anastariAI (_Creature); +} + + +void AddSC_boss_baroness_anastari() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_baroness_anastari"; + newscript->GetAI = GetAI_boss_baroness_anastari; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_cannon_master_willey.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_cannon_master_willey.cpp new file mode 100644 index 00000000000..91d4b4fd397 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_cannon_master_willey.cpp @@ -0,0 +1,221 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: boss_cannon_master_willey +SD%Complete: 100 +SDComment: +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +//front, left +#define ADD_1X 3553.851807 +#define ADD_1Y -2945.885986 +#define ADD_1Z 125.001015 +#define ADD_1O 0.592007 +//front, right +#define ADD_2X 3559.206299 +#define ADD_2Y -2952.929932 +#define ADD_2Z 125.001015 +#define ADD_2O 0.592007 +//mid, left +#define ADD_3X 3552.417480 +#define ADD_3Y -2948.667236 +#define ADD_3Z 125.001015 +#define ADD_3O 0.592007 +//mid, right +#define ADD_4X 3555.651855 +#define ADD_4Y -2953.519043 +#define ADD_4Z 125.001015 +#define ADD_4O 0.592007 +//back, left +#define ADD_5X 3547.927246 +#define ADD_5Y -2950.977295 +#define ADD_5Z 125.001015 +#define ADD_5O 0.592007 +//back, mid +#define ADD_6X 3553.094697 +#define ADD_6Y -2952.123291 +#define ADD_6Z 125.001015 +#define ADD_6O 0.592007 +//back, right +#define ADD_7X 3552.727539 +#define ADD_7Y -2957.776123 +#define ADD_7Z 125.001015 +#define ADD_7O 0.592007 +//behind, left +#define ADD_8X 3547.156250 +#define ADD_8Y -2953.162354 +#define ADD_8Z 125.001015 +#define ADD_8O 0.592007 +//behind, right +#define ADD_9X 3550.202148 +#define ADD_9Y -2957.437744 +#define ADD_9Z 125.001015 +#define ADD_9O 0.592007 + +#define SPELL_KNOCKAWAY 10101 +#define SPELL_PUMMEL 15615 +#define SPELL_SHOOT 20463 +//#define SPELL_SUMMONCRIMSONRIFLEMAN 17279 + +struct MANGOS_DLL_DECL boss_cannon_master_willeyAI : public ScriptedAI +{ + boss_cannon_master_willeyAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 KnockAway_Timer; + uint32 Pummel_Timer; + uint32 Shoot_Timer; + uint32 SummonRifleman_Timer; + + void Reset() + { + Shoot_Timer = 1000; + Pummel_Timer = 7000; + KnockAway_Timer = 11000; + SummonRifleman_Timer = 15000; + } + + void JustDied(Unit* Victim) + { + m_creature->SummonCreature(11054,ADD_1X,ADD_1Y,ADD_1Z,ADD_1O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_2X,ADD_2Y,ADD_2Z,ADD_2O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_3X,ADD_3Y,ADD_3Z,ADD_3O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_4X,ADD_4Y,ADD_4Z,ADD_4O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_5X,ADD_5Y,ADD_5Z,ADD_5O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_7X,ADD_7Y,ADD_7Z,ADD_7O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_9X,ADD_9Y,ADD_9Z,ADD_9O,TEMPSUMMON_TIMED_DESPAWN,240000); + } + + void Aggro(Unit *who) + { + } + + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Pummel + if (Pummel_Timer < diff) + { + //Cast + if (rand()%100 < 90) //90% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_PUMMEL); + } + //12 seconds until we should cast this again + Pummel_Timer = 12000; + }else Pummel_Timer -= diff; + + //KnockAway + if (KnockAway_Timer < diff) + { + //Cast + if (rand()%100 < 80) //80% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_KNOCKAWAY); + } + //14 seconds until we should cast this again + KnockAway_Timer = 14000; + }else KnockAway_Timer -= diff; + + //Shoot + if (Shoot_Timer < diff) + { + //Cast + DoCast(m_creature->getVictim(),SPELL_SHOOT); + //1 seconds until we should cast this again + Shoot_Timer = 1000; + }else Shoot_Timer -= diff; + + //SummonRifleman + if (SummonRifleman_Timer < diff) + { + //Cast + switch (rand()%9) + { + case 0: + m_creature->SummonCreature(11054,ADD_1X,ADD_1Y,ADD_1Z,ADD_1O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_2X,ADD_2Y,ADD_2Z,ADD_2O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_4X,ADD_4Y,ADD_4Z,ADD_4O,TEMPSUMMON_TIMED_DESPAWN,240000); + break; + case 1: + m_creature->SummonCreature(11054,ADD_2X,ADD_2Y,ADD_2Z,ADD_2O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_3X,ADD_3Y,ADD_3Z,ADD_3O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_5X,ADD_5Y,ADD_5Z,ADD_5O,TEMPSUMMON_TIMED_DESPAWN,240000); + break; + case 2: + m_creature->SummonCreature(11054,ADD_3X,ADD_3Y,ADD_3Z,ADD_3O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_4X,ADD_4Y,ADD_4Z,ADD_4O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_6X,ADD_6Y,ADD_6Z,ADD_6O,TEMPSUMMON_TIMED_DESPAWN,240000); + break; + case 3: + m_creature->SummonCreature(11054,ADD_4X,ADD_4Y,ADD_4Z,ADD_4O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_5X,ADD_5Y,ADD_5Z,ADD_5O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_7X,ADD_7Y,ADD_7Z,ADD_7O,TEMPSUMMON_TIMED_DESPAWN,240000); + break; + case 4: + m_creature->SummonCreature(11054,ADD_5X,ADD_5Y,ADD_5Z,ADD_5O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_6X,ADD_6Y,ADD_6Z,ADD_6O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_8X,ADD_8Y,ADD_8Z,ADD_8O,TEMPSUMMON_TIMED_DESPAWN,240000); + break; + case 5: + m_creature->SummonCreature(11054,ADD_6X,ADD_6Y,ADD_6Z,ADD_6O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_7X,ADD_7Y,ADD_7Z,ADD_7O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_9X,ADD_9Y,ADD_9Z,ADD_9O,TEMPSUMMON_TIMED_DESPAWN,240000); + break; + case 6: + m_creature->SummonCreature(11054,ADD_7X,ADD_7Y,ADD_7Z,ADD_7O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_8X,ADD_8Y,ADD_8Z,ADD_8O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_1X,ADD_1Y,ADD_1Z,ADD_1O,TEMPSUMMON_TIMED_DESPAWN,240000); + break; + case 7: + m_creature->SummonCreature(11054,ADD_8X,ADD_8Y,ADD_8Z,ADD_8O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_9X,ADD_9Y,ADD_9Z,ADD_9O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_2X,ADD_2Y,ADD_2Z,ADD_2O,TEMPSUMMON_TIMED_DESPAWN,240000); + break; + case 8: + m_creature->SummonCreature(11054,ADD_9X,ADD_9Y,ADD_9Z,ADD_9O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_1X,ADD_1Y,ADD_1Z,ADD_1O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(11054,ADD_3X,ADD_3Y,ADD_3Z,ADD_3O,TEMPSUMMON_TIMED_DESPAWN,240000); + break; + } + //30 seconds until we should cast this again + SummonRifleman_Timer = 30000; + }else SummonRifleman_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_cannon_master_willey(Creature *_Creature) +{ + return new boss_cannon_master_willeyAI (_Creature); +} + + +void AddSC_boss_cannon_master_willey() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_cannon_master_willey"; + newscript->GetAI = GetAI_boss_cannon_master_willey; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_dathrohan_balnazzar.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_dathrohan_balnazzar.cpp new file mode 100644 index 00000000000..0db8cff8141 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_dathrohan_balnazzar.cpp @@ -0,0 +1,316 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: boss_darhrohan_balnazzar +SD%Complete: 100 +SDComment: CHECK SQL +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +//Dathrohan spells +#define SPELL_CRUSADERSHAMMER 17286 //AOE stun +#define SPELL_CRUSADERSTRIKE 17281 +#define SPELL_MINDBLAST 17287 +#define SPELL_HOLYSTRIKE 17284 //weapon dmg +3 +#define SPELL_DAZED 1604 + +//Transform +#define SPELL_BALNAZZARTRANSFORM 17288 //restore full HP/mana, trigger spell Balnazzar Transform Stun + +//Balnazzar spells +#define SPELL_SHADOWSHOCK 20603 //AOE 740-860dmg +#define SPELL_PSYCHICSCREAM 15398 //One target, might want to make a code selecting random target +#define SPELL_DEEPSLEEP 24777 //AOE, ten sec +#define SPELL_SHADOWBOLTVOLLEY 20741 //AOE, 255-345dmg +//#define SPELL_MINDCONTROL 15690 //core support needed + +//Summon +//G1 front, left +#define ADD_1X 3444.156250 +#define ADD_1Y -3090.626709 +#define ADD_1Z 135.002319 +#define ADD_1O 2.240888 +//G1 front, right +#define ADD_2X 3449.123535 +#define ADD_2Y -3087.009766 +#define ADD_2Z 135.002319 +#define ADD_2O 2.240888 +//G1 back left +#define ADD_3X 3446.246826 +#define ADD_3Y -3093.466309 +#define ADD_3Z 135.002319 +#define ADD_3O 2.240888 +//G1 back, right +#define ADD_4X 3451.160889 +#define ADD_4Y -3089.904785 +#define ADD_4Z 135.002136 +#define ADD_4O 2.240888 +//G2 front, left +#define ADD_5X 3457.995117 +#define ADD_5Y -3080.916504 +#define ADD_5Z 135.002319 +#define ADD_5O 3.784981 +//G2 front, right +#define ADD_6X 3454.302490 +#define ADD_6Y -3076.330566 +#define ADD_6Z 135.002319 +#define ADD_6O 3.784981 +//G2 back left +#define ADD_7X 3460.975098 +#define ADD_7Y -3078.901367 +#define ADD_7Z 135.002319 +#define ADD_7O 3.784981 +//G2 back, right +#define ADD_8X 3457.338867 +#define ADD_8Y -3073.979004 +#define ADD_8Z 135.002319 +#define ADD_8O 3.784981 + +struct MANGOS_DLL_DECL boss_dathrohan_balnazzarAI : public ScriptedAI +{ + boss_dathrohan_balnazzarAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 CrusadersHammer_Timer; + uint32 CrusaderStrike_Timer; + uint32 MindBlast_Timer; + uint32 HolyStrike_Timer; + uint32 Dazed_Timer; + uint32 ShadowShock_Timer; + uint32 PsychicScream_Timer; + uint32 DeepSleep_Timer; + uint32 ShadowBoltVolley_Timer; + // uint32 MindControl_Timer; + bool Transformed; + + void Reset() + { + CrusadersHammer_Timer = 8000; + CrusaderStrike_Timer = 14000; + MindBlast_Timer = 17000; + HolyStrike_Timer = 18000; + Dazed_Timer = 23000; + ShadowShock_Timer = 4000; + PsychicScream_Timer = 16000; + DeepSleep_Timer = 20000; + ShadowBoltVolley_Timer = 9000; + // MindControl_Timer = 10000; + Transformed = false; + + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,10545); + m_creature->SetFloatValue(OBJECT_FIELD_SCALE_X, 1.00f); + + } + + void JustDied(Unit* Victim) + { + m_creature->SummonCreature(10698,ADD_1X,ADD_1Y,ADD_1Z,ADD_1O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(10698,ADD_2X,ADD_2Y,ADD_2Z,ADD_2O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(10698,ADD_3X,ADD_3Y,ADD_3Z,ADD_3O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(10698,ADD_4X,ADD_4Y,ADD_4Z,ADD_4O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(10698,ADD_5X,ADD_5Y,ADD_5Z,ADD_5O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(10698,ADD_6X,ADD_6Y,ADD_6Z,ADD_6O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(10698,ADD_7X,ADD_7Y,ADD_7Z,ADD_7O,TEMPSUMMON_TIMED_DESPAWN,240000); + m_creature->SummonCreature(10698,ADD_8X,ADD_8Y,ADD_8Z,ADD_8O,TEMPSUMMON_TIMED_DESPAWN,240000); + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //START NOT TRANSFORMED + if (!Transformed) + { + //CrusadersHammer + if (CrusadersHammer_Timer < diff && !m_creature->IsNonMeleeSpellCasted(false)) + { + //Cast + if (rand()%100 < 75) //50% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_CRUSADERSHAMMER); + } + //15 seconds until we should cast this again + CrusadersHammer_Timer = 12000; + }else CrusadersHammer_Timer -= diff; + + //CrusaderStrike + if (CrusaderStrike_Timer < diff && !m_creature->IsNonMeleeSpellCasted(false)) + { + //Cast + if (rand()%100 < 60) //50% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_CRUSADERSTRIKE); + } + //15 seconds until we should cast this again + CrusaderStrike_Timer = 15000; + }else CrusaderStrike_Timer -= diff; + + //MindBlast + if (MindBlast_Timer < diff && !m_creature->IsNonMeleeSpellCasted(false)) + { + //Cast + if (rand()%100 < 70) //70% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_MINDBLAST); + } + //15 seconds until we should cast this again + MindBlast_Timer = 10000; + }else MindBlast_Timer -= diff; + + //HolyStrike + if (HolyStrike_Timer < diff && !m_creature->IsNonMeleeSpellCasted(false)) + { + //Cast + if (rand()%100 < 50) //50% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_HOLYSTRIKE); + } + //15 seconds until we should cast this again + HolyStrike_Timer = 15000; + }else HolyStrike_Timer -= diff; + + //Dazed + if (Dazed_Timer < diff && !m_creature->IsNonMeleeSpellCasted(false)) + { + //Cast + if (rand()%100 < 50) //50% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_DAZED); + } + //15 seconds until we should cast this again + Dazed_Timer = 15000; + }else Dazed_Timer -= diff; + + //BalnazzarTransform + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 40) + { + //Cast + DoCast(m_creature,SPELL_BALNAZZARTRANSFORM); //restore hp, mana and stun + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,10691); //then change disaply id + m_creature->SetFloatValue(OBJECT_FIELD_SCALE_X, 3.00f); //then, change size + Transformed = true; + } + + //START ELSE TRANSFORMED + } else { + + + //MindBlast + if (MindBlast_Timer < diff && !m_creature->IsNonMeleeSpellCasted(false)) + { + //Cast + if (rand()%100 < 60) //70% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_MINDBLAST); + } + //15 seconds until we should cast this again + MindBlast_Timer = 10000; + }else MindBlast_Timer -= diff; + + //ShadowShock + if (ShadowShock_Timer < diff) + { + //Cast + if (rand()%100 < 80) //80% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_SHADOWSHOCK); + } + //15 seconds until we should cast this again + ShadowShock_Timer = 11000; + }else ShadowShock_Timer -= diff; + + //PsychicScream + if (PsychicScream_Timer < diff) + { + //Cast + if (rand()%100 < 60) //60% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_PSYCHICSCREAM); + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-50); + } + //15 seconds until we should cast this again + PsychicScream_Timer = 20000; + }else PsychicScream_Timer -= diff; + + //DeepSleep + if (DeepSleep_Timer < diff) + { + //Cast + if (rand()%100 < 55) //55% chance to cast + { + //Cast + Unit* target = NULL; + + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + DoCast(target,SPELL_DEEPSLEEP); + } + //15 seconds until we should cast this again + DeepSleep_Timer = 15000; + }else DeepSleep_Timer -= diff; + + //ShadowBoltVolley + if (ShadowBoltVolley_Timer < diff) + { + //Cast + if (rand()%100 < 75) //75% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_SHADOWBOLTVOLLEY); + } + //15 seconds until we should cast this again + ShadowBoltVolley_Timer = 13000; + }else ShadowBoltVolley_Timer -= diff; + + //MindControl + // if (MindControl_Timer < diff) + // { + //Cast + // if (rand()%100 < 50) //50% chance to cast + // { + // DoCast(m_creature->getVictim(),SPELL_MINDCONTROL); + // } + //15 seconds until we should cast this again + // MindControl_Timer = 15000; + // }else MindControl_Timer -= diff; + + //END ELSE TRANSFORMED + } + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_dathrohan_balnazzar(Creature *_Creature) +{ + return new boss_dathrohan_balnazzarAI (_Creature); +} + +void AddSC_boss_dathrohan_balnazzar() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_dathrohan_balnazzar"; + newscript->GetAI = GetAI_boss_dathrohan_balnazzar; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_magistrate_barthilas.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_magistrate_barthilas.cpp new file mode 100644 index 00000000000..aceb05b1614 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_magistrate_barthilas.cpp @@ -0,0 +1,114 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: boss_magistrate_barthilas +SD%Complete: 100 +SDComment: +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_DRAININGBLOW 16793 +#define SPELL_CROWDPUMMEL 10887 +#define SPELL_MIGHTYBLOW 14099 +#define SPELL_DAZED 1604 + +struct MANGOS_DLL_DECL boss_magistrate_barthilasAI : public ScriptedAI +{ + boss_magistrate_barthilasAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 DrainingBlow_Timer; + uint32 CrowdPummel_Timer; + uint32 MightyBlow_Timer; + uint32 Dazed_Timer; + + void Reset() + { + DrainingBlow_Timer = 4000; + CrowdPummel_Timer = 13000; + MightyBlow_Timer = 11000; + Dazed_Timer = 7000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //DrainingBlow + if (DrainingBlow_Timer < diff) + { + //Cast + DoCast(m_creature->getVictim(),SPELL_DRAININGBLOW); + + //4 seconds until we should cast this again + DrainingBlow_Timer = 4000; + }else DrainingBlow_Timer -= diff; + + //CrowdPummel + if (CrowdPummel_Timer < diff) + { + //Cast + DoCast(m_creature->getVictim(),SPELL_CROWDPUMMEL); + + //13 seconds until we should cast this agian + CrowdPummel_Timer = 13000; + }else CrowdPummel_Timer -= diff; + + //MightyBlow + if (MightyBlow_Timer < diff) + { + //Cast + DoCast(m_creature->getVictim(),SPELL_MIGHTYBLOW); + + //11 seconds until we should cast this again + MightyBlow_Timer = 11000; + }else MightyBlow_Timer -= diff; + + //Dazed + if (Dazed_Timer < diff) + { + //Cast + DoCast(m_creature->getVictim(),SPELL_DAZED); + + //20 seconds until we should cast this again + Dazed_Timer = 20000; + }else Dazed_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_magistrate_barthilas(Creature *_Creature) +{ + return new boss_magistrate_barthilasAI (_Creature); +} + + +void AddSC_boss_magistrate_barthilas() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_magistrate_barthilas"; + newscript->GetAI = GetAI_boss_magistrate_barthilas; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_maleki_the_pallid.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_maleki_the_pallid.cpp new file mode 100644 index 00000000000..6c3c75e9bc6 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_maleki_the_pallid.cpp @@ -0,0 +1,119 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: boss_maleki_the_pallid +SD%Complete: 100 +SDComment: +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FROSTNOVA 22645 +#define SPELL_FROSTBOLT 17503 +#define SPELL_DRAINLIFE 20743 +#define SPELL_ICETOMB 16869 + +struct MANGOS_DLL_DECL boss_maleki_the_pallidAI : public ScriptedAI +{ + boss_maleki_the_pallidAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 FrostNova_Timer; + uint32 Frostbolt_Timer; + uint32 IceTomb_Timer; + uint32 DrainLife_Timer; + + void Reset() + { + FrostNova_Timer = 11000; + Frostbolt_Timer = 1000; + IceTomb_Timer = 16000; + DrainLife_Timer = 31000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //FrostNova + if (FrostNova_Timer < diff) + { + //Cast + DoCast(m_creature->getVictim(),SPELL_FROSTNOVA); + //23 seconds until we should cast this again + FrostNova_Timer = 23000; + }else FrostNova_Timer -= diff; + + //Frostbolt + if (Frostbolt_Timer < diff) + { + //Cast + if (rand()%100 < 90) //90% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_FROSTBOLT); + } + //3.5 seconds until we should cast this again + Frostbolt_Timer = 3500; + }else Frostbolt_Timer -= diff; + + //IceTomb + if (IceTomb_Timer < diff) + { + //Cast + if (rand()%100 < 65) //65% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_ICETOMB); + } + //28 seconds until we should cast this again + IceTomb_Timer = 28000; + }else IceTomb_Timer -= diff; + + //DrainLife + if (DrainLife_Timer < diff) + { + //Cast + if (rand()%100 < 55) //55% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_DRAINLIFE); + } + //31 seconds until we should cast this again + DrainLife_Timer = 31000; + }else DrainLife_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_maleki_the_pallid(Creature *_Creature) +{ + return new boss_maleki_the_pallidAI (_Creature); +} + + +void AddSC_boss_maleki_the_pallid() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_maleki_the_pallid"; + newscript->GetAI = GetAI_boss_maleki_the_pallid; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_nerubenkan.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_nerubenkan.cpp new file mode 100644 index 00000000000..59bd632836c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_nerubenkan.cpp @@ -0,0 +1,138 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: boss_nerubenkan +SD%Complete: 100 +SDComment: +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_ENCASINGWEBS 4962 +#define SPELL_PIERCEARMOR 6016 +#define SPELL_VIRULENTPOISON 16427 +//#define SPELL_RAISEUNDEADSCARAB 17235 + +struct MANGOS_DLL_DECL boss_nerubenkanAI : public ScriptedAI +{ + boss_nerubenkanAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 EncasingWebs_Timer; + uint32 PierceArmor_Timer; + uint32 VirulentPoison_Timer; + uint32 RaiseUndeadScarab_Timer; + int Rand; + int RandX; + int RandY; + Creature* Summoned; + + void Reset() + { + VirulentPoison_Timer = 3000; + EncasingWebs_Timer = 7000; + PierceArmor_Timer = 19000; + RaiseUndeadScarab_Timer = 11000; + } + + void Aggro(Unit *who) + { + } + + void RaiseUndeadScarab(Unit* victim) + { + Rand = rand()%10; + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = rand()%10; + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + Summoned = DoSpawnCreature(10876, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 180000); + if(Summoned) + ((CreatureAI*)Summoned->AI())->AttackStart(victim); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //EncasingWebs + if (EncasingWebs_Timer < diff) + { + //Cast + DoCast(m_creature->getVictim(),SPELL_ENCASINGWEBS); + //30 seconds until we should cast this again + EncasingWebs_Timer = 30000; + }else EncasingWebs_Timer -= diff; + + //PierceArmor + if (PierceArmor_Timer < diff) + { + //Cast + if (rand()%100 < 75) //75% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_PIERCEARMOR); + } + //35 seconds until we should cast this again + PierceArmor_Timer = 35000; + }else PierceArmor_Timer -= diff; + + //VirulentPoison + if (VirulentPoison_Timer < diff) + { + //Cast + DoCast(m_creature->getVictim(),SPELL_VIRULENTPOISON); + //20 seconds until we should cast this again + VirulentPoison_Timer = 20000; + }else VirulentPoison_Timer -= diff; + + //RaiseUndeadScarab + if (RaiseUndeadScarab_Timer < diff) + { + //Cast + RaiseUndeadScarab(m_creature->getVictim()); + //16 seconds until we should cast this again + RaiseUndeadScarab_Timer = 16000; + }else RaiseUndeadScarab_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_nerubenkan(Creature *_Creature) +{ + return new boss_nerubenkanAI (_Creature); +} + + +void AddSC_boss_nerubenkan() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_nerubenkan"; + newscript->GetAI = GetAI_boss_nerubenkan; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_order_of_silver_hand.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_order_of_silver_hand.cpp new file mode 100644 index 00000000000..9da5b48e55c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_order_of_silver_hand.cpp @@ -0,0 +1,161 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: Boss_Silver_Hand_Bosses +SD%Complete: 40 +SDComment: Basic script to have support for Horde paladin epic mount (quest 9737). All 5 members of Order of the Silver Hand running this script (least for now) +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" +#include "def_stratholme.h" + +/*##### +# Additional: +# Although this is a working solution, the correct would be in addition to check if Aurius is dead. +# Once player extinguish the eternal flame (cast spell 31497->start event 11206) Aurius should become hostile. +# Once Aurius is defeated, he should be the one summoning the ghosts. +#####*/ + +#define SH_GREGOR 17910 +#define SH_CATHELA 17911 +#define SH_NEMAS 17912 +#define SH_AELMAR 17913 +#define SH_VICAR 17914 +#define SH_QUEST_CREDIT 17915 + +#define SPELL_HOLY_LIGHT 25263 +#define SPELL_DIVINE_SHIELD 13874 + +struct MANGOS_DLL_DECL boss_silver_hand_bossesAI : public ScriptedAI +{ + boss_silver_hand_bossesAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 HolyLight_Timer; + uint32 DivineShield_Timer; + + void Reset() + { + HolyLight_Timer = 20000; + DivineShield_Timer = 20000; + + if(pInstance) + { + switch(m_creature->GetEntry()) + { + case SH_AELMAR: + pInstance->SetData(TYPE_SH_AELMAR, 0); + break; + case SH_CATHELA: + pInstance->SetData(TYPE_SH_CATHELA, 0); + break; + case SH_GREGOR: + pInstance->SetData(TYPE_SH_GREGOR, 0); + break; + case SH_NEMAS: + pInstance->SetData(TYPE_SH_NEMAS, 0); + break; + case SH_VICAR: + pInstance->SetData(TYPE_SH_VICAR, 0); + break; + } + } + } + + void Aggro(Unit* who) + { + } + + void JustDied(Unit* Killer) + { + if(pInstance) + { + switch(m_creature->GetEntry()) + { + case SH_AELMAR: + pInstance->SetData(TYPE_SH_AELMAR, 2); + break; + case SH_CATHELA: + pInstance->SetData(TYPE_SH_CATHELA, 2); + break; + case SH_GREGOR: + pInstance->SetData(TYPE_SH_GREGOR, 2); + break; + case SH_NEMAS: + pInstance->SetData(TYPE_SH_NEMAS, 2); + break; + case SH_VICAR: + pInstance->SetData(TYPE_SH_VICAR, 2); + break; + } + if(pInstance->GetData(TYPE_SH_QUEST) && Killer->GetTypeId() == TYPEID_PLAYER) + ((Player*)Killer)->KilledMonster(SH_QUEST_CREDIT,m_creature->GetGUID()); + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if (HolyLight_Timer < diff) + { + if (m_creature->GetHealth()*5 < m_creature->GetMaxHealth()) + { + DoCast(m_creature, SPELL_HOLY_LIGHT); + HolyLight_Timer = 20000; + } + }else HolyLight_Timer -= diff; + + if (DivineShield_Timer < diff) + { + if (m_creature->GetHealth()*20 < m_creature->GetMaxHealth()) + { + DoCast(m_creature, SPELL_DIVINE_SHIELD); + DivineShield_Timer = 40000; + } + }else DivineShield_Timer -= diff; + + DoMeleeAttackIfReady(); + } + +}; +CreatureAI* GetAI_boss_silver_hand_bossesAI(Creature *_Creature) +{ + return new boss_silver_hand_bossesAI (_Creature); +} + +/*##### +# +#####*/ + +void AddSC_boss_order_of_silver_hand() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_silver_hand_bosses"; + newscript->GetAI = GetAI_boss_silver_hand_bossesAI; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_postmaster_malown.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_postmaster_malown.cpp new file mode 100644 index 00000000000..c303207101d --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_postmaster_malown.cpp @@ -0,0 +1,144 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: boss_postmaster_malown +SD%Complete: 50 +SDComment: +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +//Spell ID to summon this guy is 24627 "Summon Postmaster Malown" +//He should be spawned along with three other elites once the third postbox has been opened + +#define SAY_MALOWNED "You just got MALOWNED!" + +#define SPELL_WAILINGDEAD 7713 +#define SPELL_BACKHAND 6253 +#define SPELL_CURSEOFWEAKNESS 8552 +#define SPELL_CURSEOFTONGUES 12889 +#define SPELL_CALLOFTHEGRAVE 17831 + +struct MANGOS_DLL_DECL boss_postmaster_malownAI : public ScriptedAI +{ + boss_postmaster_malownAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 WailingDead_Timer; + uint32 Backhand_Timer; + uint32 CurseOfWeakness_Timer; + uint32 CurseOfTongues_Timer; + uint32 CallOfTheGrave_Timer; + bool HasYelled; + + void Reset() + { + WailingDead_Timer = 19000; //lasts 6 sec + Backhand_Timer = 8000; //2 sec stun + CurseOfWeakness_Timer = 20000; //lasts 2 mins + CurseOfTongues_Timer = 22000; + CallOfTheGrave_Timer = 25000; + HasYelled = false; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //WailingDead + if (WailingDead_Timer < diff) + { + //Cast + if (rand()%100 < 65) //65% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_WAILINGDEAD); + } + //19 seconds until we should cast this again + WailingDead_Timer = 19000; + }else WailingDead_Timer -= diff; + + //Backhand + if (Backhand_Timer < diff) + { + //Cast + if (rand()%100 < 45) //45% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_BACKHAND); + } + //8 seconds until we should cast this again + Backhand_Timer = 8000; + }else Backhand_Timer -= diff; + + //CurseOfWeakness + if (CurseOfWeakness_Timer < diff) + { + //Cast + if (rand()%100 < 3) //3% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_CURSEOFWEAKNESS); + } + //20 seconds until we should cast this again + CurseOfWeakness_Timer = 20000; + }else CurseOfWeakness_Timer -= diff; + + //CurseOfTongues + if (CurseOfTongues_Timer < diff) + { + //Cast + if (rand()%100 < 3) //3% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_CURSEOFTONGUES); + } + //22 seconds until we should cast this again + CurseOfTongues_Timer = 22000; + }else CurseOfTongues_Timer -= diff; + + //CallOfTheGrave + if (CallOfTheGrave_Timer < diff) + { + //Cast + if (rand()%100 < 5) //5% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_CALLOFTHEGRAVE); + } + //25 seconds until we should cast this again + CallOfTheGrave_Timer = 25000; + }else CallOfTheGrave_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_postmaster_malown(Creature *_Creature) +{ + return new boss_postmaster_malownAI (_Creature); +} + + +void AddSC_boss_postmaster_malown() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_postmaster_malown"; + newscript->GetAI = GetAI_boss_postmaster_malown; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_ramstein_the_gorger.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_ramstein_the_gorger.cpp new file mode 100644 index 00000000000..7b175f5b9ff --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_ramstein_the_gorger.cpp @@ -0,0 +1,92 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: boss_ramstein_the_gorger +SD%Complete: 100 +SDComment: +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_TRAMPLE 15550 +#define SPELL_KNOCKOUT 17307 + +struct MANGOS_DLL_DECL boss_ramstein_the_gorgerAI : public ScriptedAI +{ + boss_ramstein_the_gorgerAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Trample_Timer; + uint32 Knockout_Timer; + + void Reset() + { + Trample_Timer = 3000; + Knockout_Timer = 12000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Trample + if (Trample_Timer < diff) + { + //Cast + if (rand()%100 < 75) //75% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_TRAMPLE); + } + //7 seconds until we should cast this again + Trample_Timer = 7000; + }else Trample_Timer -= diff; + + //Knockout + if (Knockout_Timer < diff) + { + //Cast + if (rand()%100 < 70) //70% chance to cast + { + DoCast(m_creature->getVictim(),SPELL_KNOCKOUT); + } + //10 seconds until we should cast this again + Knockout_Timer = 10000; + }else Knockout_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_ramstein_the_gorger(Creature *_Creature) +{ + return new boss_ramstein_the_gorgerAI (_Creature); +} + + +void AddSC_boss_ramstein_the_gorger() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_ramstein_the_gorger"; + newscript->GetAI = GetAI_boss_ramstein_the_gorger; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/boss_timmy_the_cruel.cpp b/src/bindings/scripts/scripts/zone/stratholme/boss_timmy_the_cruel.cpp new file mode 100644 index 00000000000..1fc4eedc5f5 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/boss_timmy_the_cruel.cpp @@ -0,0 +1,103 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: boss_timmy_the_cruel +SD%Complete: 100 +SDComment: +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" + +#define SAY_SPAWN "TIMMY!" + +#define SPELL_RAVENOUSCLAW 17470 + +struct MANGOS_DLL_DECL boss_timmy_the_cruelAI : public ScriptedAI +{ + boss_timmy_the_cruelAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 RavenousClaw_Timer; + bool HasYelled; + + void Reset() + { + RavenousClaw_Timer = 10000; + HasYelled = false; + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + if (!HasYelled) + { + DoYell(SAY_SPAWN,LANG_UNIVERSAL,NULL); + HasYelled = true; + } + + //Begin melee attack if we are within range + DoStartAttackAndMovement(who); + } + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //RavenousClaw + if (RavenousClaw_Timer < diff) + { + //Cast + DoCast(m_creature->getVictim(),SPELL_RAVENOUSCLAW); + //15 seconds until we should cast this again + RavenousClaw_Timer = 15000; + }else RavenousClaw_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_timmy_the_cruel(Creature *_Creature) +{ + return new boss_timmy_the_cruelAI (_Creature); +} + + +void AddSC_boss_timmy_the_cruel() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_timmy_the_cruel"; + newscript->GetAI = GetAI_boss_timmy_the_cruel; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/def_stratholme.h b/src/bindings/scripts/scripts/zone/stratholme/def_stratholme.h new file mode 100644 index 00000000000..27c181a77f5 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/def_stratholme.h @@ -0,0 +1,14 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* This program is free software licensed under GPL version 2 +* Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_STRATHOLME_H +#define DEF_STRATHOLME_H + +#define TYPE_SH_AELMAR 1 +#define TYPE_SH_CATHELA 2 +#define TYPE_SH_GREGOR 3 +#define TYPE_SH_NEMAS 4 +#define TYPE_SH_VICAR 5 +#define TYPE_SH_QUEST 6 +#endif diff --git a/src/bindings/scripts/scripts/zone/stratholme/instance_stratholme.cpp b/src/bindings/scripts/scripts/zone/stratholme/instance_stratholme.cpp new file mode 100644 index 00000000000..8277d6e043a --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/instance_stratholme.cpp @@ -0,0 +1,77 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: instance_stratholme +SD%Complete: 100 +SDComment: +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" +#include "def_stratholme.h" + +struct MANGOS_DLL_DECL instance_stratholme : public ScriptedInstance +{ + instance_stratholme(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + bool IsSilverHandDead[5]; + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case TYPE_SH_AELMAR: + IsSilverHandDead[0] = (data) ? true : false; + break; + case TYPE_SH_CATHELA: + IsSilverHandDead[1] = (data) ? true : false; + break; + case TYPE_SH_GREGOR: + IsSilverHandDead[2] = (data) ? true : false; + break; + case TYPE_SH_NEMAS: + IsSilverHandDead[3] = (data) ? true : false; + break; + case TYPE_SH_VICAR: + IsSilverHandDead[4] = (data) ? true : false; + break; + } + } + + uint32 GetData(uint32 type) + { + if(type == TYPE_SH_QUEST) + if(IsSilverHandDead[0] && IsSilverHandDead[1] && IsSilverHandDead[2] && IsSilverHandDead[3] && IsSilverHandDead[4]) + return true; + + return false; + } +}; + +InstanceData* GetInstanceData_instance_stratholme(Map* map) +{ + return new instance_stratholme(map); +} + +void AddSC_instance_stratholme() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_stratholme"; + newscript->GetInstanceData = GetInstanceData_instance_stratholme; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/stratholme/stratholme.cpp b/src/bindings/scripts/scripts/zone/stratholme/stratholme.cpp new file mode 100644 index 00000000000..33e88bc04be --- /dev/null +++ b/src/bindings/scripts/scripts/zone/stratholme/stratholme.cpp @@ -0,0 +1,335 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 +* 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 +*/ + +/* ScriptData +SDName: Stratholme +SD%Complete: 100 +SDComment: trash mobs for strat. Simple AI mobs converted to EAI (except Mindless Skeleton and Thuzadin Acolyte) +SDCategory: Stratholme +EndScriptData */ + +#include "precompiled.h" +#include "../../creature/simple_ai.h" + +/*###### +## mob_freed_soul +######*/ + +//Possibly more of these quotes around. +#define SAY_ZAPPED0 "Thanks to Egan" +#define SAY_ZAPPED1 "Rivendare must die" +#define SAY_ZAPPED2 "Who you gonna call?" +#define SAY_ZAPPED3 "Don't cross those beams!" + +struct MANGOS_DLL_DECL mob_freed_soulAI : public ScriptedAI +{ + mob_freed_soulAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + + switch (rand()%4) + { + case 0: + DoSay(SAY_ZAPPED0,LANG_UNIVERSAL,NULL); + break; + case 1: + DoSay(SAY_ZAPPED1,LANG_UNIVERSAL,NULL); + break; + case 2: + DoSay(SAY_ZAPPED2,LANG_UNIVERSAL,NULL); + break; + case 3: + DoSay(SAY_ZAPPED3,LANG_UNIVERSAL,NULL); + break; + } + } + + void Aggro(Unit* who) + { + } +}; +CreatureAI* GetAI_mob_freed_soul(Creature *_Creature) +{ + return new mob_freed_soulAI (_Creature); +} +/*###### +## mob_restless_soul +######*/ + +struct MANGOS_DLL_DECL mob_restless_soulAI : public ScriptedAI +{ + mob_restless_soulAI(Creature *c) : ScriptedAI(c) {Reset();} + + Unit* PlayerHolder; + uint32 Die_Timer; + bool OkToDie; + + void Reset() + { + Die_Timer = 10000; + OkToDie = false; + + PlayerHolder = NULL; + } + + void Aggro(Unit* who) + { + } + + void SummonFreedSoul(Unit* victim) + { + int Rand; + int RandX; + int RandY; + + Rand = rand()%1; + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = rand()%1; + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + DoSpawnCreature(11136, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 300000); + } + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + if (caster->GetTypeId() == TYPEID_PLAYER) + { + if(!PlayerHolder && !OkToDie && spell->Id == 17368 && ((Player*)caster)->GetQuestStatus(5282) == QUEST_STATUS_INCOMPLETE) + { + PlayerHolder = caster; + OkToDie = true; + Die_Timer = 5000; + } + } + return; + } + + void JustDied(Unit* Killer) + { + if (Killer->GetTypeId() == TYPEID_PLAYER) + { + //check quest status + if(PlayerHolder == ((Player*)Killer) && ((Player*)Killer)->GetQuestStatus(5282) == QUEST_STATUS_INCOMPLETE) + { + SummonFreedSoul(m_creature->getVictim()); + } + } + } + + void UpdateAI(const uint32 diff) + { + if (PlayerHolder && OkToDie && Die_Timer < diff) + { + PlayerHolder->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + else Die_Timer -= diff; + + return; + } +}; +CreatureAI* GetAI_mob_restless_soul(Creature *_Creature) +{ + return new mob_restless_soulAI (_Creature); +} + +/*###### +## mobs_spectral_ghostly_citizen +######*/ + +struct MANGOS_DLL_DECL mobs_spectral_ghostly_citizenAI : public ScriptedAI +{ + mobs_spectral_ghostly_citizenAI(Creature *c) : ScriptedAI(c) {Reset();} + + Unit* PlayerHolder; + uint32 Die_Timer; + bool OkToDie; + + void Reset() + { + Die_Timer = 5000; + OkToDie = false; + PlayerHolder = NULL; + } + + void Aggro(Unit* who) + { + } + + void SummonRestlessSoul(Unit* victim) + { + int Rand; + int RandX; + int RandY; + + Rand = rand()%7; + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = rand()%7; + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + DoSpawnCreature(11122, RandX, RandY, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 600000); + } + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + if (caster->GetTypeId() == TYPEID_PLAYER) + { + if(!PlayerHolder && !OkToDie && spell->Id == 17368 && ((Player*)caster)->GetQuestStatus(5282) == QUEST_STATUS_INCOMPLETE) + { + PlayerHolder = caster; + OkToDie = true; + Die_Timer = 5000; + } + } + return; + } + + void JustDied(Unit* Killer) + { + if (Killer->GetTypeId() == TYPEID_PLAYER) + { + //check quest status + if(PlayerHolder == ((Player*)Killer) && ((Player*)Killer)->GetQuestStatus(5282) == QUEST_STATUS_INCOMPLETE) + { + SummonRestlessSoul(m_creature->getVictim()); //always one + if (rand()%100 < 90) SummonRestlessSoul(m_creature->getVictim()); //90% a second + if (rand()%100 < 50) SummonRestlessSoul(m_creature->getVictim()); //50% a third + if (rand()%100 < 30) SummonRestlessSoul(m_creature->getVictim()); //30% a fourth + } + } + } + + void UpdateAI(const uint32 diff) + { + if (PlayerHolder && OkToDie && Die_Timer < diff) + { + PlayerHolder->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + else Die_Timer -= diff; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_mobs_spectral_ghostly_citizen(Creature *_Creature) +{ + return new mobs_spectral_ghostly_citizenAI (_Creature); +} +bool ReciveEmote_mobs_spectral_ghostly_citizen(Player *player, Creature *_Creature, uint32 emote) +{ + _Creature->HandleEmoteCommand(emote); + + if (emote == TEXTEMOTE_DANCE) + ((mobs_spectral_ghostly_citizenAI*)_Creature->AI())->EnterEvadeMode(); + + return true; +} + +/*###### +## mob_mindless_skeleton +######*/ + +CreatureAI* GetAI_mob_mindless_skeleton(Creature *_Creature) +{ + SimpleAI* ai = new SimpleAI (_Creature); + + //dazed + ai->Spell[0].Enabled = true; + ai->Spell[0].Spell_Id = 13496; + ai->Spell[0].Cooldown = 15000; + ai->Spell[0].First_Cast = 3000; + ai->Spell[0].Cast_Target_Type = CAST_HOSTILE_TARGET; + + ai->EnterEvadeMode(); + + return ai; +} + +/*###### +## mob_thuzadin_acolyte +######*/ + +CreatureAI* GetAI_mob_thuzadin_acolyte(Creature *_Creature) +{ + SimpleAI* ai = new SimpleAI (_Creature); + + //dazed + ai->Spell[0].Enabled = true; + ai->Spell[0].Spell_Id = 13496; + ai->Spell[0].Cooldown = 15000; + ai->Spell[0].First_Cast = 1000; + ai->Spell[0].Cast_Target_Type = CAST_HOSTILE_TARGET; + + ai->EnterEvadeMode(); + + return ai; +} + +/*###### +## +######*/ + +void AddSC_stratholme() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mob_freed_soul"; + newscript->GetAI = GetAI_mob_freed_soul; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_restless_soul"; + newscript->GetAI = GetAI_mob_restless_soul; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mobs_spectral_ghostly_citizen"; + newscript->GetAI = GetAI_mobs_spectral_ghostly_citizen; + newscript->pReceiveEmote = &ReciveEmote_mobs_spectral_ghostly_citizen; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_mindless_skeleton"; + newscript->GetAI = GetAI_mob_mindless_skeleton; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_thuzadin_acolyte"; + newscript->GetAI = GetAI_mob_thuzadin_acolyte; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/sunwell_plateau/boss_brutallus.cpp b/src/bindings/scripts/scripts/zone/sunwell_plateau/boss_brutallus.cpp new file mode 100644 index 00000000000..36a8238cf45 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/sunwell_plateau/boss_brutallus.cpp @@ -0,0 +1,188 @@ +/* Copyright © 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Brutallus +SD%Complete: 80 +SDComment: Needs intro. Talk to the dragon. +EndScriptData */ + +#include "precompiled.h" +#include "def_sunwell_plateau.h" + +// Yells and Sounds used by boss. +#define YELL_AGGRO "Ahh! More lambs to the slaughter!" +#define SOUND_AGGRO 12463 + +#define YELL_BERSERK "So much for a real challenge... Die!" +#define SOUND_BERSERK 12470 + +#define YELL_KILL1 "Perish, insect!" +#define SOUND_KILL1 12464 + +#define YELL_KILL2 "You are meat!" +#define SOUND_KILL2 12465 + +#define YELL_KILL3 "Too easy!" +#define SOUND_KILL3 12466 + +#define YELL_CHARGE "I will crush you!" //I think it use this for stomp. No? +#define SOUND_CHARGE 12460 + +#define YELL_DEATH "Gah! Well done... Now... this gets... interesting..." +#define SOUND_DEATH 12471 + +#define YELL_LOVE1 "Bring the fight to me!" +#define SOUND_LOVE1 12467 + +#define YELL_LOVE2 "Another day, another glorious battle!" +#define SOUND_LOVE2 12468 + +#define YELL_LOVE3 "I live for this!" +#define SOUND_LOVE3 12469 + +// Boss spells. +#define METEOR_SLASH 45150 +#define BURN 46394 +#define STOMP 45185 +#define BERSERK 26662 + +struct MANGOS_DLL_DECL boss_brutallusAI : public ScriptedAI +{ + boss_brutallusAI(Creature *c) : ScriptedAI(c) { Reset(); } + + uint32 SlashTimer; + uint32 BurnTimer; + uint32 StompTimer; + uint32 BerserkTimer; + uint32 LoveTimer; + + void Reset() + { + SlashTimer = 11000; + StompTimer = 30000; + BurnTimer = 60000; + BerserkTimer = 360000; + LoveTimer = 10000 + rand()%7000; + } + + void Aggro(Unit *who) + { + DoYell(YELL_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%3) + { + case 0: + DoYell(YELL_KILL1,LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL1); + break; + case 1: + DoYell(YELL_KILL2,LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL2); + break; + case 2: + DoYell(YELL_KILL3,LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL3); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(YELL_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if(LoveTimer < diff) + { + switch(rand()%3) + { + case 0: + DoYell(YELL_LOVE1,LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_LOVE1); + break; + case 1: + DoYell(YELL_LOVE2,LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_LOVE2); + break; + case 2: + DoYell(YELL_LOVE3,LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_LOVE3); + break; + } + LoveTimer = 15000 + rand()%8000; + }else LoveTimer -= diff; + + if(SlashTimer < diff) + { + DoCast(m_creature->getVictim(),METEOR_SLASH); + SlashTimer = 11000; + }else SlashTimer -= diff; + + if(StompTimer < diff) + { + DoYell(YELL_CHARGE,LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CHARGE); + Unit *Target = m_creature->getVictim(); + DoCast(Target,STOMP); + if(Target->HasAura(45151,0)) Target->RemoveAura(45151,0); + StompTimer = 30000; + }else StompTimer -= diff; + + if(BurnTimer < diff) + { + Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0); + DoCast(target,BURN); + BurnTimer = 60000; + } + else BurnTimer -= diff; + + DoMeleeAttackIfReady(); + + if(BerserkTimer < diff) + { + DoYell(YELL_BERSERK,LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_BERSERK); + DoCast(m_creature,BERSERK); + BerserkTimer = 20000; + } + else BerserkTimer -= diff; + } + +}; + +CreatureAI* GetAI_boss_brutallus(Creature *_Creature) +{ + return new boss_brutallusAI (_Creature); +} + +void AddSC_boss_brutallus() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_brutallus"; + newscript->GetAI = GetAI_boss_brutallus; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/sunwell_plateau/boss_kalecgos.cpp b/src/bindings/scripts/scripts/zone/sunwell_plateau/boss_kalecgos.cpp new file mode 100644 index 00000000000..6b35c91ac58 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/sunwell_plateau/boss_kalecgos.cpp @@ -0,0 +1,657 @@ +/* Copyright ? 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Kalecgos +SD%Complete: 0 +SDComment: VERIFY SCRIPT +SDCategory: Sunwell Plateau +EndScriptData */ + +#include "precompiled.h" +#include "WorldPacket.h" +#include "def_sunwell_plateau.h" + +#define SAY_KALECGOS_AGGRO "Aggh! No longer will I be a slave to Malygos! Challenge me and you will be destroyed!" +#define SOUND_KALECGOS_AGGRO 12422 +#define SAY_KALECGOS_SPELL1 "I will purge you!" +#define SOUND_KALECGOS_SPELL1 12423 +#define SAY_KALECGOS_SPELL2 "Your pain has only begun!" +#define SOUND_KALECGOS_SPELL2 12424 +#define SAY_KALECGOS_SLAY1 "In the name of Kil'jaeden!" +#define SOUND_KALECGOS_SLAY1 12425 +#define SAY_KALECGOS_SLAY2 "You were warned!" +#define SOUND_KALECGOS_SLAY2 12426 +#define SAY_KALECGOS_ENRAGE "My awakening is complete! You shall all perish!" +#define SOUND_KALECGOS_ENRAGE 12427 + +#define SAY_SATH_AGGRO "There will be no reprieve. My work here is nearly finished." +#define SOUND_SATH_AGGRO 12451 +#define SAY_SATH_DEATH "I'm... never on... the losing... side..." +#define SOUND_SATH_DEATH 12452 +#define SAY_SATH_SPELL1 "Your misery is my delight!" +#define SOUND_SATH_SPELL1 12453 +#define SAY_SATH_SPELL2 "I will watch you bleed!" +#define SOUND_SATH_SPELL2 12454 +#define SAY_SATH_SLAY1 "Pitious mortal!" +#define SOUND_SATH_SLAY1 12455 +#define SAY_SATH_SLAY2 "Haven't you heard? I always win!" +#define SOUND_SATH_SLAY2 12456 +#define SAY_SATH_ENRAGE "I have toyed with you long enough!" +#define SOUND_SATH_ENRAGE 12457 + +#define SAY_KALEC_AGGRO "I need... your help... Cannot... resist him... much longer..." +#define SOUND_KALEC_AGGRO 12428 +#define SAY_KALEC_NEAR_DEATH "Aaahhh! Help me, before I lose my mind!" +#define SOUND_KALEC_NEAR_DEATH 12429 + //??? +#define SAY_KALEC_NEAR_DEATH2 "Hurry! There is not much of me left!" +#define SOUND_KALEC_NEAR_DEATH2 12430 +#define SAY_KALEC_PLRWIN "I am forever in your debt. Once we have triumphed over Kil'jaeden, this entire world will be in your debt as well." +#define SOUND_KALEC_PLRWIN 12431 + +#define SPELL_SPECTRAL_EXHAUSTION 44867 +#define SPELL_TELEPORT_SPECTRAL_REALM 46019 +#define SPELL_SPECTRAL_REALM 46021 + +#define NOTIFY_SPECTRALLY_EXHAUSTED "Your body is too exhausted to travel to the Spectral Realm." +#define ERROR_INST_DATA "SD2: Instance Data not set properly for Sunwell Plateau. Kalecgos Encounter will be buggy." +#define ERROR_INST_DATA_PLR "SD2 ERROR: Instance Data not set properly for Sunwell Plateau. Please report this to your administrator." +#define ERROR_UNABLE_TO_TELEPORT "SD2: Unable to select target for Spectral Blast. Threatlist has too few players." +#define ERROR_MISSING_TELEPORT_GUID "SD2: [Kalecgos] Invalid TeleportTargetGUID. Unable to teleport player." +#define ERROR_KALECGOS_NOT_FOUND "SD2: Unable to create pointer to Kalecgos from Sathrovarr." + +#define KALECGOS_ARENA_X 1704.34 +#define KALECGOS_ARENA_Y 928.17 +#define KALECGOS_ARENA_Z 53.08 + +/*** Kalecgos ****/ +#define SPELL_SPECTRAL_BLAST 44866 +#define SPELL_ARCANE_BUFFET 45018 +#define SPELL_FROST_BREATH 44799 +#define SPELL_HEROIC_STRIKE 45026 +#define SPELL_REVITALIZE 45027 +#define SPELL_TAIL_LASH 45122 +#define SPELL_TRANSFORM_KALEC 45027 +#define SPELL_CRAZED_RAGE 44806 +uint32 WildMagic[]= { 44978, 45001, 45002, 45004, 45006, 45010 }; + +/*** Sathrovarr ***/ +#define SPELL_CORRUPTING_STRIKE 45029 +#define SPELL_CURSE_OF_BOUNDLESS_AGONY 45032 +#define SPELL_SHADOW_BOLT_VOLLEY 45031 + +/*** Misc ***/ +#define SPELL_BANISH 44836 + +void TeleportToInnerVeil(Player* plr) +{ + if(plr->HasAura(SPELL_SPECTRAL_EXHAUSTION, 0)) + { + plr->GetSession()->SendNotification(NOTIFY_SPECTRALLY_EXHAUSTED); + return; + } + + ScriptedInstance* pInstance = ((ScriptedInstance*)plr->GetInstanceData()); + if(!pInstance) + { + error_log(ERROR_INST_DATA); + plr->GetSession()->SendNotification(ERROR_INST_DATA_PLR); + return; + } + + pInstance->SetData64(DATA_PLAYER_SPECTRAL_REALM, plr->GetGUID()); + // Remove the player from Kalecgos' Threat List + Creature* Kalecgos = ((Creature*)Unit::GetUnit(*plr, pInstance->GetData64(DATA_KALECGOS_DRAGON))); + if(Kalecgos) + { + HostilReference* ref = Kalecgos->getThreatManager().getOnlineContainer().getReferenceByTarget(plr); + if(ref) + ref->removeReference(); + } + + // Add the player to Sathrovarr's Threat List + Creature* Sathrovarr = ((Creature*)Unit::GetUnit(*plr, pInstance->GetData64(DATA_SATHROVARR))); + if(Sathrovarr) + Sathrovarr->AddThreat(plr, 1.0f); + + // Make them able to see Sathrovarr (he's invisible for some reason). Also, when this buff wears off, they get teleported back to Normal Realm (this is handled by Instance Script) + plr->CastSpell(plr, SPELL_SPECTRAL_REALM, true); + plr->CastSpell(plr, SPELL_TELEPORT_SPECTRAL_REALM, true); +} + +bool GOHello_GO_Spectral_Portal(Player* plr, GameObject* go) +{ + TeleportToInnerVeil(plr); + + return true; +} + +struct MANGOS_DLL_DECL boss_kalecgosAI : public ScriptedAI +{ + boss_kalecgosAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint64 TeleportTargetGUID; + + uint32 ArcaneBuffetTimer; + uint32 FrostBreathTimer; + uint32 WildMagicTimer; + uint32 SpectralBlastTimer; + uint32 SpectralTeleportTimer; + uint32 ForceFieldTimer; + uint32 ExitTimer; + + bool LockedArena; + bool Uncorrupted; + bool Banished; + bool Checked; + bool Enraged; + + void Reset() + { + TeleportTargetGUID = 0; + + // TODO: Fix timers + ArcaneBuffetTimer = 8000; + FrostBreathTimer = 24000; + WildMagicTimer = 18000; + SpectralBlastTimer = 30000; + SpectralTeleportTimer = SpectralBlastTimer + 2000; + + ForceFieldTimer = 20000; + ExitTimer = 0; + + LockedArena = false; + Uncorrupted = false; + Banished = false; + Checked = false; + Enraged = false; + + // Reset Sathrovarr too + if(pInstance) + if(Creature* Sath = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_SATHROVARR)))) + Sath->AI()->EnterEvadeMode(); + } + + void Aggro(Unit* who) + { + DoYell(SAY_KALECGOS_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KALECGOS_AGGRO); + + if(pInstance) + pInstance->SetData(DATA_KALECGOS_EVENT, IN_PROGRESS); + } + + void DamageTaken(Unit* done_by, uint32 &damage) + { + if(damage > m_creature->GetHealth() && done_by != m_creature) + { + if(!Uncorrupted) + { + damage = 0; + Banished = true; + DoCast(m_creature, SPELL_BANISH, true); + m_creature->GetMotionMaster()->MoveIdle(); + } + else + { + damage = 0; + BeginOutro(); + } + } + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_KALECGOS_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_KALECGOS_SLAY1); + break; + case 1: + DoYell(SAY_KALECGOS_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_KALECGOS_SLAY2); + break; + } + } + + void BeginOutro() + { + debug_log("SD2: KALEC: Beginning Outro"); + + if(!pInstance) + { + error_log(ERROR_INST_DATA); + return; + } + + Unit* Sathrovarr = Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_SATHROVARR)); + if(Sathrovarr) + { + Sathrovarr->DealDamage(Sathrovarr, Sathrovarr->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + Sathrovarr->Relocate(KALECGOS_ARENA_X, KALECGOS_ARENA_Y, KALECGOS_ARENA_Z); + Sathrovarr->SendMonsterMove(KALECGOS_ARENA_X, KALECGOS_ARENA_Y, KALECGOS_ARENA_Z, 0, 0, 0); + } + + Creature* Kalec = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_KALECGOS_HUMAN))); + if(Kalec) + { + Kalec->DeleteThreatList(); + Kalec->SetVisibility(VISIBILITY_OFF); + } + + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->setFaction(35); + } + + void MovementInform(uint32 type, uint32 id) + { + if(type != POINT_MOTION_TYPE) + return; + + if(id) + { + if(pInstance) + { + GameObject* ForceField = GameObject::GetGameObject(*m_creature, pInstance->GetData64(DATA_GO_FORCEFIELD)); + if(ForceField) + ForceField->SetGoState(1); + + pInstance->SetData(DATA_KALECGOS_EVENT, DONE); + } + else error_log(ERROR_INST_DATA); + + m_creature->SetVisibility(VISIBILITY_OFF); + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->getVictim() || !m_creature->SelectHostilTarget() || Banished) + return; + + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 10) && !Enraged) + { + Unit* Sathrovarr = Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_SATHROVARR)); + if(Sathrovarr) + Sathrovarr->CastSpell(Sathrovarr, SPELL_CRAZED_RAGE, true); + DoCast(m_creature, SPELL_CRAZED_RAGE, true); + Enraged = true; + } + + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 1) && !Checked) + { + Checked = true; + + if(!Uncorrupted) + { + Banished = true; + DoCast(m_creature, SPELL_BANISH, true); + m_creature->GetMotionMaster()->MoveIdle(); + } + else + BeginOutro(); + } + + if(ExitTimer) + if(ExitTimer <= diff) + { + debug_log("SD2: KALEC: Exiting the arena"); + DoYell(SAY_KALEC_PLRWIN, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KALEC_PLRWIN); + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT + MOVEMENTFLAG_LEVITATING); + float x, y, z; + float iniX, iniY, iniZ; + m_creature->GetPosition(iniX, iniY, iniZ); + m_creature->GetRandomPoint(iniX, iniY, iniZ, 30, x, y, z); + z = 70; + m_creature->GetMotionMaster()->MovePoint(1, x, y, z); + }else ExitTimer -= diff; + + if(!LockedArena) + if(ForceFieldTimer < diff) + { + if(pInstance) + { + GameObject* ForceField = GameObject::GetGameObject((*m_creature), pInstance->GetData64(DATA_GO_FORCEFIELD)); + if(ForceField) + ForceField->SetUInt32Value(GAMEOBJECT_STATE, 0); + + LockedArena = true; + }else error_log(ERROR_INST_DATA); + }else ForceFieldTimer -= diff; + + if(ArcaneBuffetTimer < diff) + { + if(rand()%3 == 0) + { + DoYell(SAY_KALECGOS_SPELL1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KALECGOS_SPELL1); + } + DoCast(m_creature->getVictim(), SPELL_ARCANE_BUFFET); + ArcaneBuffetTimer = 20000; + }else ArcaneBuffetTimer -= diff; + + if(FrostBreathTimer < diff) + { + if(rand()%2 == 0) + { + DoYell(SAY_KALECGOS_SPELL2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KALECGOS_SPELL2); + } + DoCast(m_creature->getVictim(), SPELL_FROST_BREATH); + FrostBreathTimer = 25000; + }else FrostBreathTimer -= diff; + + if(WildMagicTimer < diff) + { + DoCast(m_creature->getVictim(), WildMagic[rand()%6]); + WildMagicTimer = 19000; + }else WildMagicTimer -= diff; + + if(SpectralBlastTimer < diff) + { + if(Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1)) + { + TeleportTargetGUID = target->GetGUID(); + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, TeleportTargetGUID); + SpectralBlastTimer = 30000; + SpectralTeleportTimer = 2000; + } + }else SpectralBlastTimer -= diff; + + if(SpectralTeleportTimer < diff) + { + if(TeleportTargetGUID) + { + Unit* pUnit = Unit::GetUnit((*m_creature), TeleportTargetGUID); + if(pUnit) + { + pUnit->CastSpell(pUnit, SPELL_SPECTRAL_BLAST, true); + TeleportToInnerVeil((Player*)pUnit); + } + else error_log(ERROR_MISSING_TELEPORT_GUID); + } + else error_log(ERROR_MISSING_TELEPORT_GUID); + + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->getVictim()->GetGUID()); + TeleportTargetGUID = 0; + SpectralTeleportTimer = SpectralBlastTimer + 2000; + + }else SpectralTeleportTimer -= diff; + + if(!Banished) DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_sathrovarrAI : public ScriptedAI +{ + boss_sathrovarrAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 CorruptingStrikeTimer; + uint32 CurseOfBoundlessAgonyTimer; + uint32 ShadowBoltVolleyTimer; + bool Banished; + bool Enraged; + + void Reset() + { + // FIXME: Timers + CorruptingStrikeTimer = 5000; + CurseOfBoundlessAgonyTimer = 15000; + ShadowBoltVolleyTimer = 10000; + + Banished = false; + Enraged = false; + + DoCast(m_creature, SPELL_SPECTRAL_REALM, true); + } + + void Aggro(Unit* who) + { + DoYell(SAY_SATH_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SATH_AGGRO); + + Creature* Kalec = ((Creature*)Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_KALECGOS_HUMAN))); + if(Kalec) + { + m_creature->AddThreat(Kalec, 10000000.0f); + Kalec->AddThreat(m_creature, 10000000.0f); + } + } + + void DamageTaken(Unit* done_by, uint32 &damage) + { + if(damage > m_creature->GetHealth()) + { + damage = 0; + DoCast(m_creature, SPELL_BANISH, true); + Banished = true; + + DoYell(SAY_SATH_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SATH_DEATH); + + if(!pInstance) + { + error_log(ERROR_INST_DATA); + return; + } + + pInstance->SetData(DATA_SET_SPECTRAL_CHECK, 5000); + + Creature* Kalecgos = ((Creature*)Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KALECGOS_DRAGON))); + if(Kalecgos) + { + ((boss_kalecgosAI*)Kalecgos->AI())->Checked = false; + ((boss_kalecgosAI*)Kalecgos->AI())->Uncorrupted = true; + } + else error_log(ERROR_KALECGOS_NOT_FOUND); + } + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SATH_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SATH_SLAY1); + break; + case 1: + DoYell(SAY_SATH_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SATH_SLAY2); + break; + } + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->getVictim() || !m_creature->SelectHostilTarget() || Banished) + return; + + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 10) && !Enraged) + { + Unit* Kalecgos = Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_KALECGOS_DRAGON)); + if(Kalecgos) + Kalecgos->CastSpell(Kalecgos, SPELL_CRAZED_RAGE, true); + DoCast(m_creature, SPELL_CRAZED_RAGE, true); + Enraged = true; + } + + if(CorruptingStrikeTimer < diff) + { + if(rand()%2 == 0) + { + DoYell(SAY_SATH_SPELL2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SATH_SPELL2); + } + DoCast(m_creature->getVictim(), SPELL_CORRUPTING_STRIKE); + CorruptingStrikeTimer = 13000; + }else CorruptingStrikeTimer -= diff; + + if(CurseOfBoundlessAgonyTimer < diff) + { + DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_CURSE_OF_BOUNDLESS_AGONY); + CurseOfBoundlessAgonyTimer = 35000; + DoCast(m_creature, SPELL_SPECTRAL_REALM, true); + }else CurseOfBoundlessAgonyTimer -= diff; + + if(ShadowBoltVolleyTimer < diff) + { + if(rand()%2 == 0) + { + DoYell(SAY_SATH_SPELL1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SATH_SPELL1); + } + DoCast(m_creature->getVictim(), SPELL_SHADOW_BOLT_VOLLEY); + ShadowBoltVolleyTimer = 15000; + }else ShadowBoltVolleyTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_kalecAI : public ScriptedAI +{ + boss_kalecAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 RevitalizeTimer; + uint32 HeroicStrikeTimer; + + bool HasYelled10Percent; + bool HasYelled20Percent; + + void Reset() + { + //TODO: Times! + RevitalizeTimer = 30000; + HeroicStrikeTimer = 8000; + + HasYelled10Percent = false; + HasYelled20Percent = false; + + DoCast(m_creature, SPELL_SPECTRAL_REALM, true); + } + + void Aggro(Unit* who) + { + DoYell(SAY_KALEC_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KALEC_AGGRO); + } + + void JustDied(Unit* killer) + { + // Whatever happens when Kalec (Half-elf) dies + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->getVictim() || !m_creature->SelectHostilTarget()) + return; + + if(RevitalizeTimer < diff) + { + if(pInstance) + { + Unit* pUnit = Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_RANDOM_SPECTRAL_PLAYER)); + if(pUnit) + DoCast(pUnit, SPELL_REVITALIZE); + RevitalizeTimer = 30000; + } + }else RevitalizeTimer -= diff; + + if(HeroicStrikeTimer < diff) + { + DoCast(m_creature->getVictim(), SPELL_HEROIC_STRIKE); + HeroicStrikeTimer = 30000; + }else HeroicStrikeTimer -= diff; + + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20) && !HasYelled20Percent) + { + DoYell(SAY_KALEC_NEAR_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KALEC_NEAR_DEATH); + HasYelled20Percent = true; + } + + if(((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 10) && !HasYelled10Percent) + { + DoYell(SAY_KALEC_NEAR_DEATH2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KALEC_NEAR_DEATH2); + HasYelled10Percent = true; + } + } +}; + +CreatureAI* GetAI_boss_kalecgos(Creature* c) +{ + return new boss_kalecgosAI(c); +} + +CreatureAI* GetAI_boss_sathrovarr(Creature* c) +{ + return new boss_sathrovarrAI(c); +} + +CreatureAI* GetAI_boss_kalec(Creature* c) +{ + return new boss_kalecAI(c); +} + +void AddSC_boss_kalecgos() +{ + Script* newscript; + + newscript = new Script; + newscript->GetAI = GetAI_boss_kalecgos; + newscript->Name = "boss_kalecgos"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->GetAI = GetAI_boss_sathrovarr; + newscript->Name = "boss_sathrovarr"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->GetAI = GetAI_boss_kalec; + newscript->Name = "boss_kalec"; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->pGOHello = GOHello_GO_Spectral_Portal; + newscript->Name = "go_spectral_portal"; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/sunwell_plateau/def_sunwell_plateau.h b/src/bindings/scripts/scripts/zone/sunwell_plateau/def_sunwell_plateau.h new file mode 100644 index 00000000000..316b13062f0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/sunwell_plateau/def_sunwell_plateau.h @@ -0,0 +1,43 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_SUNWELLPLATEAU_H +#define DEF_SUNWELLPLATEAU_H + +/*** Encounters ***/ +#define DATA_KALECGOS_EVENT 0 +#define DATA_BRUTALLUS_EVENT 1 +#define DATA_FELMYST_EVENT 2 +#define DATA_EREDAR_TWINS_EVENT 3 +#define DATA_MURU_EVENT 4 +#define DATA_KILJAEDEN_EVENT 5 + +/*** Creatures ***/ +#define DATA_KALECGOS_DRAGON 6 +#define DATA_KALECGOS_HUMAN 7 +#define DATA_SATHROVARR 8 +#define DATA_BRUTALLUS 9 +#define DATA_FELMYST 10 +#define DATA_ALYTHESS 11 +#define DATA_SACROLASH 12 +#define DATA_MURU 13 +#define DATA_KILJAEDEN 14 +#define DATA_KILJAEDEN_CONTROLLER 15 +#define DATA_ANVEENA 16 + +/*** GameObjects ***/ +#define DATA_GO_FORCEFIELD 17 +#define DATA_GO_FIRE_BARRIER 18 +#define DATA_GATE_1 19 +#define DATA_GATE_2 20 +#define DATA_GATE_3 21 +#define DATA_GATE_4 22 +#define DATA_GATE_5 23 + +/*** Misc ***/ +#define DATA_PLAYER_SPECTRAL_REALM 24 +#define DATA_SET_SPECTRAL_CHECK 25 +#define DATA_RANDOM_SPECTRAL_PLAYER 26 +#define DATA_INST_EJECT_PLAYERS 27 +#endif diff --git a/src/bindings/scripts/scripts/zone/sunwell_plateau/instance_sunwell_plateau.cpp b/src/bindings/scripts/scripts/zone/sunwell_plateau/instance_sunwell_plateau.cpp new file mode 100644 index 00000000000..d5c419520ec --- /dev/null +++ b/src/bindings/scripts/scripts/zone/sunwell_plateau/instance_sunwell_plateau.cpp @@ -0,0 +1,275 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +/* ScriptData +SDName: instance_sunwell_plateau +SD%Complete: 0 +SDComment: VERIFY SCRIPT +SDCategory: Sunwell_Plateau +EndScriptData */ + +#include "precompiled.h" +#include "def_sunwell_plateau.h" + +#define ENCOUNTERS 6 + +#define SPELL_SPECTRAL_REALM 46021 +#define SPELL_TELEPORT_NORMAL_REALM 46020 +#define SPELL_SPECTRAL_EXHAUSTION 44867 + +/* Sunwell Plateau: +0 - Kalecgos and Sathrovarr +1 - Brutallus +2 - Felmyst +3 - Eredar Twins (Alythess and Sacrolash) +4 - M'uru +5 - Kil'Jaeden +*/ + +struct MANGOS_DLL_DECL instance_sunwell_plateau : public ScriptedInstance +{ + instance_sunwell_plateau(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint32 Encounters[ENCOUNTERS]; + + /** Creatures **/ + uint64 Kalecgos_Dragon; + uint64 Kalecgos_Human; + uint64 Sathrovarr; + uint64 Brutallus; + uint64 Felmyst; + uint64 Alythess; + uint64 Sacrolash; + uint64 Muru; + uint64 KilJaeden; + uint64 KilJaedenController; + uint64 Anveena; + + /** GameObjects **/ + uint64 ForceField; // Kalecgos Encounter + uint64 FireBarrier; // Brutallus Encounter + uint64 Gate[5]; // Rename this to be more specific after door placement is verified. + + /*** Misc ***/ + uint32 SpectralRealmTimer; + std::vector SpectralRealmList; + + void Initialize() + { + /*** Creatures ***/ + Kalecgos_Dragon = 0; + Kalecgos_Human = 0; + Sathrovarr = 0; + Brutallus = 0; + Felmyst = 0; + Alythess = 0; + Sacrolash = 0; + Muru = 0; + KilJaeden = 0; + KilJaedenController = 0; + Anveena = 0; + + /*** GameObjects ***/ + ForceField = 0; + FireBarrier = 0; + Gate[0] = 0; // TODO: Rename Gate[n] with gate_ for better specificity + Gate[1] = 0; + Gate[2] = 0; + Gate[3] = 0; + Gate[4] = 0; + + /*** Encounters ***/ + for(uint8 i = 0; i < ENCOUNTERS; ++i) + Encounters[i] = NOT_STARTED; + + /*** Misc ***/ + SpectralRealmTimer = 5000; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < ENCOUNTERS; ++i) + if(Encounters[i] == IN_PROGRESS) + return true; + + return false; + } + + void OnCreatureCreate(Creature* creature, uint32 entry) + { + switch(entry) + { + case 24850: Kalecgos_Dragon = creature->GetGUID(); break; + case 24891: Kalecgos_Human = creature->GetGUID(); break; + case 24892: Sathrovarr = creature->GetGUID(); break; + case 24882: Brutallus = creature->GetGUID(); break; + case 25038: Felmyst = creature->GetGUID(); break; + case 25166: Alythess = creature->GetGUID(); break; + case 25165: Sacrolash = creature->GetGUID(); break; + case 25741: Muru = creature->GetGUID(); break; + case 25315: KilJaeden = creature->GetGUID(); break; + case 25608: KilJaedenController = creature->GetGUID(); break; + case 26046: Anveena = creature->GetGUID(); break; + } + } + + void OnObjectCreate(GameObject* gobj) + { + switch(gobj->GetEntry()) + { + case 188421: ForceField = gobj->GetGUID(); break; + case 188075: FireBarrier = gobj->GetGUID(); break; + case 187979: Gate[0] = gobj->GetGUID(); break; + case 187770: Gate[1] = gobj->GetGUID(); break; + case 187896: Gate[2] = gobj->GetGUID(); break; + case 187990: Gate[3] = gobj->GetGUID(); break; + case 188118: Gate[4] = gobj->GetGUID(); break; + } + } + + uint32 GetData(uint32 id) + { + switch(id) + { + case DATA_KALECGOS_EVENT: return Encounters[0]; break; + case DATA_BRUTALLUS_EVENT: return Encounters[1]; break; + case DATA_FELMYST_EVENT: return Encounters[2]; break; + case DATA_EREDAR_TWINS_EVENT: return Encounters[3]; break; + case DATA_MURU_EVENT: return Encounters[4]; break; + case DATA_KILJAEDEN_EVENT: return Encounters[5]; break; + } + + return 0; + } + + uint64 GetData64(uint32 id) + { + switch(id) + { + case DATA_KALECGOS_DRAGON: return Kalecgos_Dragon; break; + case DATA_KALECGOS_HUMAN: return Kalecgos_Human; break; + case DATA_SATHROVARR: return Sathrovarr; break; + case DATA_BRUTALLUS: return Brutallus; break; + case DATA_FELMYST: return Felmyst; break; + case DATA_ALYTHESS: return Alythess; break; + case DATA_SACROLASH: return Sacrolash; break; + case DATA_MURU: return Muru; break; + case DATA_KILJAEDEN: return KilJaeden; break; + case DATA_KILJAEDEN_CONTROLLER: return KilJaedenController; break; + case DATA_ANVEENA: return Anveena; break; + + case DATA_RANDOM_SPECTRAL_PLAYER: + return *(SpectralRealmList.begin() + rand()%SpectralRealmList.size()); + break; + } + return 0; + } + + void SetData(uint32 id, uint32 data) + { + switch(id) + { + case DATA_KALECGOS_EVENT: Encounters[0] = data; break; + case DATA_BRUTALLUS_EVENT: Encounters[1] = data; break; + case DATA_FELMYST_EVENT: Encounters[2] = data; break; + case DATA_EREDAR_TWINS_EVENT: Encounters[3] = data; break; + case DATA_MURU_EVENT: Encounters[4] = data; break; + case DATA_KILJAEDEN_EVENT: Encounters[5] = data; break; + + case DATA_SET_SPECTRAL_CHECK: SpectralRealmTimer = data; break; + case DATA_INST_EJECT_PLAYERS: EjectPlayers(); break; + } + } + + void SetData64(uint32 id, uint64 guid) + { + switch(id) + { + case DATA_PLAYER_SPECTRAL_REALM: + SpectralRealmList.push_back(guid); + break; + } + } + + // Dirty Hack as we can't use Unit::GetUnit in instance scripts due to lack of a WorldObject. + Player* DirtyHackToGetPlayerFromSpectralList(uint64 guid) + { + Player* first = ((InstanceMap*)instance)->GetPlayers().front(); + if(!first) + return NULL; + + Player* plr = ((Player*)Unit::GetUnit(*first, guid)); + if(plr) + return plr; + + return NULL; + } + + void EjectPlayer(Player* plr) + { + debug_log("SD2: INST: Ejecting Player %s from Spectral Realm", plr->GetName()); + // Remove player from Sathrovarr's threat list + Creature* Sath = ((Creature*)Unit::GetUnit(*plr, Sathrovarr)); + if(Sath && Sath->isAlive()) + { + HostilReference* ref = Sath->getThreatManager().getOnlineContainer().getReferenceByTarget(plr); + if(ref) + { + ref->removeReference(); + debug_log("SD2: INST: Deleting %s from Sathrovarr's threatlist", plr->GetName()); + } + } + + // Put player back in Kalecgos(Dragon)'s threat list + Creature* Kalecgos = ((Creature*)Unit::GetUnit(*plr, Kalecgos_Dragon)); + if(Kalecgos && Kalecgos->isAlive()) + { + debug_log("SD2: INST: Putting %s in Kalecgos' threatlist", plr->GetName()); + Kalecgos->AddThreat(plr, 1.0f); + } + + plr->CastSpell(plr, SPELL_TELEPORT_NORMAL_REALM, true); + plr->CastSpell(plr, SPELL_SPECTRAL_EXHAUSTION, true); + } + + void EjectPlayers() + { + for(uint8 i = 0; i < SpectralRealmList.size(); ++i) + { + Player* plr = DirtyHackToGetPlayerFromSpectralList(SpectralRealmList[i]); + if(plr && !plr->HasAura(SPELL_SPECTRAL_REALM, 0)) + { + EjectPlayer(plr); + SpectralRealmList.erase(SpectralRealmList.begin() + i); + } + } + + SpectralRealmList.clear(); + } + + void Update(uint32 diff) + { + // Only check for Spectral Realm if Kalecgos Encounter is running + if(Encounters[0] == IN_PROGRESS) + if(SpectralRealmTimer < diff) + { + EjectPlayers(); + SpectralRealmTimer = 5000; + }else SpectralRealmTimer -= diff; + } +}; + +InstanceData* GetInstanceData_instance_sunwell_plateau(Map* map) +{ + return new instance_sunwell_plateau(map); +} + +void AddSC_instance_sunwell_plateau() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_sunwell_plateau"; + newscript->GetInstanceData = GetInstanceData_instance_sunwell_plateau; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tanaris/tanaris.cpp b/src/bindings/scripts/scripts/zone/tanaris/tanaris.cpp new file mode 100644 index 00000000000..a5e74587a51 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tanaris/tanaris.cpp @@ -0,0 +1,402 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Tanaris +SD%Complete: 80 +SDComment: Quest support: 2954, 4005, 10277, 10279(Special flight path). Noggenfogger vendor +SDCategory: Tanaris +EndScriptData */ + +/* ContentData +mob_aquementas +npc_custodian_of_time +npc_marin_noggenfogger +npc_steward_of_time +npc_stone_watcher_of_norgannon +EndContentData */ + +#include "precompiled.h" +#include "../../npc/npc_escortAI.h" + +/*###### +## mob_aquementas +######*/ + +#define AGGRO_YELL_AQUE "Who dares awaken Aquementas?" + +#define SPELL_AQUA_JET 13586 +#define SPELL_FROST_SHOCK 15089 + +struct MANGOS_DLL_DECL mob_aquementasAI : public ScriptedAI +{ + mob_aquementasAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 SendItem_Timer; + uint32 SwitchFaction_Timer; + bool isFriendly; + + uint32 FrostShock_Timer; + uint32 AquaJet_Timer; + + void Reset() + { + SendItem_Timer = 0; + SwitchFaction_Timer = 10000; + m_creature->setFaction(35); + isFriendly = true; + + AquaJet_Timer = 5000; + FrostShock_Timer = 1000; + } + + void SendItem(Unit* receiver) + { + if (((Player*)receiver)->HasItemCount(11169,1,false) && + ((Player*)receiver)->HasItemCount(11172,11,false) && + ((Player*)receiver)->HasItemCount(11173,1,false) && + !((Player*)receiver)->HasItemCount(11522,1,true)) + { + ItemPosCountVec dest; + uint8 msg = ((Player*)receiver)->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, 11522, 1, false); + if( msg == EQUIP_ERR_OK ) + ((Player*)receiver)->StoreNewItem( dest, 11522, 1, true); + } + } + + void Aggro(Unit* who) + { + DoYell(AGGRO_YELL_AQUE,LANG_UNIVERSAL,who); + } + + void UpdateAI(const uint32 diff) + { + if( isFriendly ) + { + if( SwitchFaction_Timer < diff ) + { + m_creature->setFaction(91); + isFriendly = false; + }else SwitchFaction_Timer -= diff; + } + + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( !isFriendly ) + { + if( SendItem_Timer < diff ) + { + if( m_creature->getVictim()->GetTypeId() == TYPEID_PLAYER ) + SendItem(m_creature->getVictim()); + SendItem_Timer = 5000; + }else SendItem_Timer -= diff; + } + + if( FrostShock_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_FROST_SHOCK); + FrostShock_Timer = 15000; + }else FrostShock_Timer -= diff; + + if( AquaJet_Timer < diff ) + { + DoCast(m_creature,SPELL_AQUA_JET); + AquaJet_Timer = 15000; + }else AquaJet_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_mob_aquementas(Creature *_Creature) +{ + return new mob_aquementasAI (_Creature); +} + +/*###### +## npc_custodian_of_time +######*/ + +#define WHISPER_CUSTODIAN_1 "Greetings, $N. I will guide you through the cavern. Please try and keep up." +#define WHISPER_CUSTODIAN_2 "We do not know if the Caverns of Time have always been accessible to mortals. Truly, it is impossible to tell as the Timeless One is in perpetual motion, changing our timeways as he sees fit. What you see now may very well not exist tomorrow. You may wake up and have no memory of this place." +#define WHISPER_CUSTODIAN_3 "It is strange, I know... Most mortals cannot actually comprehend what they see here, as often, what they see is not anchored within their own perception of reality." +#define WHISPER_CUSTODIAN_4 "Follow me, please." +#define WHISPER_CUSTODIAN_5 "There are only two truths to be found here: First, that time is chaotic, always in flux, and completely malleable and second, perception does not dictate reality." +#define WHISPER_CUSTODIAN_6 "As custodians of time, we watch over and care for Nozdormu's realm. The master is away at the moment, which means that attempts are being made to dramatically alter time. The master never meddles in the affairs of mortals but instead corrects the alterations made to time by others. He is reactionary in this regard." +#define WHISPER_CUSTODIAN_7 "For normal maintenance of time, the Keepers of Time are sufficient caretakers. We are able to deal with most ordinary disturbances. I speak of little things, such as rogue mages changing something in the past to elevate their status or wealth in the present." +#define WHISPER_CUSTODIAN_8 "These tunnels that you see are called timeways. They are infinite in number. The ones that currently exist in your reality are what the master has deemed as 'trouble spots.' These trouble spots may differ completely in theme but they always share a cause. That is, their existence is a result of the same temporal disturbance. Remember that should you venture inside one..." +#define WHISPER_CUSTODIAN_9 "This timeway is in great disarray! We have agents inside right now attempting to restore order. What information I have indicates that Thrall's freedom is in jeopardy. A malevolent organization known as the Infinite Dragonflight is trying to prevent his escape. I fear without outside assistance, all will be lost." +#define WHISPER_CUSTODIAN_10 "We have very little information on this timeway. Sa'at has been dispatched and is currently inside. The data we have gathered from his correspondence is that the Infinite Dragonflight are once again attempting to alter time. Could it be that the opening of the Dark Portal is being targeted for sabotage? Let us hope not..." +#define WHISPER_CUSTODIAN_11 "This timeway is currently collapsing. What that may hold for the past, present and future is currently unknown..." +#define WHISPER_CUSTODIAN_12 "The timeways are currently ranked in order from least catastrophic to most catastrophic. Note that they are all classified as catastrophic, meaning that any single one of these timeways collapsing would mean that your world would end. We only classify them in such a way so that the heroes and adventurers that are sent here know which timeway best suits their abilities." +#define WHISPER_CUSTODIAN_13 "All we know of this timeway is that it leads to Mount Hyjal. The Infinite Dragonflight have gone to great lengths to prevent our involvement. We know next to nothing, mortal. Soridormi is currently attempting to break through the timeway's defenses but has thus far been unsuccessful. You might be our only hope of breaking through and resolving the conflict." +#define WHISPER_CUSTODIAN_14 "Our time is at an end $N. I would wish you luck, if such a thing existed." + +struct MANGOS_DLL_DECL npc_custodian_of_timeAI : public npc_escortAI +{ + npc_custodian_of_timeAI(Creature *c) : npc_escortAI(c) { Reset(); } + + void WaypointReached(uint32 i) + { + Unit *pTemp = Unit::GetUnit(*m_creature,PlayerGUID); + if( !pTemp ) + return; + + switch( i ) + { + case 0: DoWhisper(WHISPER_CUSTODIAN_1, pTemp); break; + case 1: DoWhisper(WHISPER_CUSTODIAN_2, pTemp); break; + case 2: DoWhisper(WHISPER_CUSTODIAN_3, pTemp); break; + case 3: DoWhisper(WHISPER_CUSTODIAN_4, pTemp); break; + case 5: DoWhisper(WHISPER_CUSTODIAN_5, pTemp); break; + case 6: DoWhisper(WHISPER_CUSTODIAN_6, pTemp); break; + case 7: DoWhisper(WHISPER_CUSTODIAN_7, pTemp); break; + case 8: DoWhisper(WHISPER_CUSTODIAN_8, pTemp); break; + case 9: DoWhisper(WHISPER_CUSTODIAN_9, pTemp); break; + case 10: DoWhisper(WHISPER_CUSTODIAN_4, pTemp); break; + case 13: DoWhisper(WHISPER_CUSTODIAN_10, pTemp); break; + case 14: DoWhisper(WHISPER_CUSTODIAN_4, pTemp); break; + case 16: DoWhisper(WHISPER_CUSTODIAN_11, pTemp); break; + case 17: DoWhisper(WHISPER_CUSTODIAN_12, pTemp); break; + case 18: DoWhisper(WHISPER_CUSTODIAN_4, pTemp); break; + case 22: DoWhisper(WHISPER_CUSTODIAN_13, pTemp); break; + case 23: DoWhisper(WHISPER_CUSTODIAN_4, pTemp); break; + case 24: + DoWhisper(WHISPER_CUSTODIAN_14, pTemp); + DoCast(pTemp,34883); + //below here is temporary workaround, to be removed when spell works properly + ((Player*)pTemp)->AreaExploredOrEventHappens(10277); + break; + } + } + + void MoveInLineOfSight(Unit *who) + { + if( IsBeingEscorted ) + return; + + if( who->GetTypeId() == TYPEID_PLAYER ) + { + if( ((Player*)who)->HasAura(34877,1) && ((Player*)who)->GetQuestStatus(10277) == QUEST_STATUS_INCOMPLETE ) + { + float Radius = 10.0; + if( m_creature->IsWithinDistInMap(who, Radius) ) + { + ((npc_escortAI*)(m_creature->AI()))->Start(false, false, false, who->GetGUID()); + } + } + } + } + + void Aggro(Unit* who) { } + void Reset() { } + + void UpdateAI(const uint32 diff) + { + npc_escortAI::UpdateAI(diff); + } +}; + +CreatureAI* GetAI_npc_custodian_of_time(Creature *_Creature) +{ + npc_custodian_of_timeAI* custodian_of_timeAI = new npc_custodian_of_timeAI(_Creature); + + custodian_of_timeAI->AddWaypoint(0, -8374.93,-4250.21, -204.38,5000); + custodian_of_timeAI->AddWaypoint(1, -8374.93,-4250.21, -204.38,16000); + custodian_of_timeAI->AddWaypoint(2, -8374.93,-4250.21, -204.38,10000); + custodian_of_timeAI->AddWaypoint(3, -8374.93,-4250.21, -204.38,2000); + custodian_of_timeAI->AddWaypoint(4, -8439.40,-4180.05, -209.25); + custodian_of_timeAI->AddWaypoint(5, -8437.82,-4120.84, -208.59,10000); + custodian_of_timeAI->AddWaypoint(6, -8437.82,-4120.84, -208.59,16000); + custodian_of_timeAI->AddWaypoint(7, -8437.82,-4120.84, -208.59,13000); + custodian_of_timeAI->AddWaypoint(8, -8437.82,-4120.84, -208.59,18000); + custodian_of_timeAI->AddWaypoint(9, -8437.82,-4120.84, -208.59,15000); + custodian_of_timeAI->AddWaypoint(10, -8437.82,-4120.84, -208.59,2000); + custodian_of_timeAI->AddWaypoint(11, -8467.26,-4198.63, -214.21); + custodian_of_timeAI->AddWaypoint(12, -8667.76,-4252.13, -209.56); + custodian_of_timeAI->AddWaypoint(13, -8703.71,-4234.58, -209.5,14000); + custodian_of_timeAI->AddWaypoint(14, -8703.71,-4234.58, -209.5,2000); + custodian_of_timeAI->AddWaypoint(15, -8642.81,-4304.37, -209.57); + custodian_of_timeAI->AddWaypoint(16, -8649.06,-4394.36, -208.46,6000); + custodian_of_timeAI->AddWaypoint(17, -8649.06,-4394.36, -208.46,18000); + custodian_of_timeAI->AddWaypoint(18, -8649.06,-4394.36, -208.46,2000); + custodian_of_timeAI->AddWaypoint(19, -8468.72,-4437.67, -215.45); + custodian_of_timeAI->AddWaypoint(20, -8427.54,-4426, -211.13); + custodian_of_timeAI->AddWaypoint(21, -8364.83,-4393.32, -205.91); + custodian_of_timeAI->AddWaypoint(22, -8304.54,-4357.2, -208.2,18000); + custodian_of_timeAI->AddWaypoint(23, -8304.54,-4357.2, -208.2,2000); + custodian_of_timeAI->AddWaypoint(24, -8375.42,-4250.41, -205.14,5000); + custodian_of_timeAI->AddWaypoint(25, -8375.42,-4250.41, -205.14,5000); + + return (CreatureAI*)custodian_of_timeAI; +} + +/*###### +## npc_marin_noggenfogger +######*/ + +bool GossipHello_npc_marin_noggenfogger(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( _Creature->isVendor() && player->GetQuestRewardStatus(2662) ) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_marin_noggenfogger(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if( action == GOSSIP_ACTION_TRADE ) + player->SEND_VENDORLIST( _Creature->GetGUID() ); + + return true; +} + +/*###### +## npc_steward_of_time +######*/ + +#define GOSSIP_ITEM_FLIGHT "Please take me to the master's lair." + +bool GossipHello_npc_steward_of_time(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( player->GetQuestStatus(10279) == QUEST_STATUS_INCOMPLETE || player->GetQuestRewardStatus(10279) ) + { + player->ADD_GOSSIP_ITEM(0, GOSSIP_ITEM_FLIGHT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(9978,_Creature->GetGUID()); + } + else + player->SEND_GOSSIP_MENU(9977,_Creature->GetGUID()); + + return true; +} + +bool QuestAccept_npc_steward_of_time(Player *player, Creature *creature, Quest const *quest ) +{ + if( quest->GetQuestId() == 10279 ) //Quest: To The Master's Lair + player->CastSpell(player,34891,true); //(Flight through Caverns) + + return false; +} + +bool GossipSelect_npc_steward_of_time(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_ACTION_INFO_DEF + 1 ) + player->CastSpell(player,34891,true); //(Flight through Caverns) + + return true; +} + +/*###### +## npc_stone_watcher_of_norgannon +######*/ + +#define GOSSIP_ITEM_NORGANNON_1 "What function do you serve?" +#define GOSSIP_ITEM_NORGANNON_2 "What are the Plates of Uldum?" +#define GOSSIP_ITEM_NORGANNON_3 "Where are the Plates of Uldum?" +#define GOSSIP_ITEM_NORGANNON_4 "Excuse me? We've been \"reschedueled for visitations\"? What does that mean?!" +#define GOSSIP_ITEM_NORGANNON_5 "So, what's inside Uldum?" +#define GOSSIP_ITEM_NORGANNON_6 "I will return when i have the Plates of Uldum." + +bool GossipHello_npc_stone_watcher_of_norgannon(Player *player, Creature *_Creature) +{ + if( _Creature->isQuestGiver() ) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( player->GetQuestStatus(2954) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_NORGANNON_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(1674, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_stone_watcher_of_norgannon(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_NORGANNON_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(1675, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_NORGANNON_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(1676, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_NORGANNON_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->SEND_GOSSIP_MENU(1677, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_NORGANNON_5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + player->SEND_GOSSIP_MENU(1678, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_NORGANNON_6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(1679, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(2954); + break; + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_tanaris() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mob_aquementas"; + newscript->GetAI = GetAI_mob_aquementas; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_custodian_of_time"; + newscript->GetAI = GetAI_npc_custodian_of_time; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_marin_noggenfogger"; + newscript->pGossipHello = &GossipHello_npc_marin_noggenfogger; + newscript->pGossipSelect = &GossipSelect_npc_marin_noggenfogger; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_steward_of_time"; + newscript->pGossipHello = &GossipHello_npc_steward_of_time; + newscript->pGossipSelect = &GossipSelect_npc_steward_of_time; + newscript->pQuestAccept = &QuestAccept_npc_steward_of_time; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_stone_watcher_of_norgannon"; + newscript->pGossipHello = &GossipHello_npc_stone_watcher_of_norgannon; + newscript->pGossipSelect = &GossipSelect_npc_stone_watcher_of_norgannon; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/arcatraz.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/arcatraz.cpp new file mode 100644 index 00000000000..abff86b24dd --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/arcatraz.cpp @@ -0,0 +1,612 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Arcatraz +SD%Complete: 60 +SDComment: Warden Mellichar, event controller for Skyriss event. Millhouse Manastorm. TODO: make better combatAI for Millhouse. +SDCategory: Tempest Keep, The Arcatraz +EndScriptData */ + +/* ContentData +npc_millhouse_manastorm +npc_warden_mellichar +mob_zerekethvoidzone +EndContentData */ + +#include "precompiled.h" +#include "def_arcatraz.h" + +/*##### +# npc_millhouse_manastorm +#####*/ + +#define SAY_INTRO_1 "Where in Bonzo's brass buttons am I? And who are-- yaaghh, that's one mother of a headache!" +#define SOUND_INTRO_1 11171 +#define SAY_INTRO_2 "\"Lowly\"? I don't care who you are friend, no one refers to the mighty Millhouse Manastorm as \"Lowly\"! I have no idea what goes on here, but I will gladly join your fight against this impudent imbecile! Prepare to defend yourself, cretin!" +#define SOUND_INTRO_2 11172 + +#define SAY_WATER "I just need to get some things ready first. You guys go ahead and get started. I need to summon up some water..." +#define SOUND_WATER 11173 + +#define SAY_BUFFS "Fantastic! Next, some protective spells. Yes! Now we're cookin'" +#define SOUND_BUFFS 11174 + +#define SAY_DRINK "And of course i'll need some mana. You guys are gonna love this, just wait." +#define SOUND_DRINK 11175 + +#define SAY_READY "Aaalllriiiight!! Who ordered up an extra large can of whoop-ass?" +#define SOUND_READY 11176 + +#define SAY_KILL_1 "I didn't even break a sweat on that one." +#define SOUND_KILL_1 11177 +#define SAY_KILL_2 "You guys, feel free to jump in anytime." +#define SOUND_KILL_2 11178 + +#define SAY_PYRO "I'm gonna light you up, sweet cheeks!" +#define SOUND_PYRO 11179 + +#define SAY_ICEBLOCK "Ice, ice, baby!" +#define SOUND_ICEBLOCK 11180 + +#define SAY_LOWHP "Heal me! Oh, for the love of all that is holy, HEAL me! I'm dying!" +#define SOUND_LOWHP 11181 + +#define SAY_DEATH "You'll be hearing from my lawyer..." +#define SOUND_DEATH 11182 + +#define SAY_COMPLETE "Who's bad? Who's bad? That's right: we bad!" +#define SOUND_COMPLETE 11183 + +#define SPELL_CONJURE_WATER 36879 +#define SPELL_ARCANE_INTELLECT 36880 +#define SPELL_ICE_ARMOR 36881 + +#define SPELL_ARCANE_MISSILES 33833 +#define SPELL_CONE_OF_COLD 12611 +#define SPELL_FIRE_BLAST 13341 +#define SPELL_FIREBALL 14034 +#define SPELL_FROSTBOLT 15497 +#define SPELL_PYROBLAST 33975 + +struct MANGOS_DLL_DECL npc_millhouse_manastormAI : public ScriptedAI +{ + npc_millhouse_manastormAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 EventProgress_Timer; + uint32 Phase; + bool Init; + bool LowHp; + + uint32 Pyroblast_Timer; + uint32 Fireball_Timer; + + void Reset() + { + EventProgress_Timer = 2000; + LowHp = false; + Init = false; + Phase = 1; + + Pyroblast_Timer = 1000; + Fireball_Timer = 2500; + + if( pInstance ) + { + if( pInstance->GetData(TYPE_WARDEN_2) == DONE ) + Init = true; + + if( pInstance->GetData(TYPE_HARBINGERSKYRISS) == DONE ) + { + DoYell(SAY_COMPLETE,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_COMPLETE); + } + } + } + + void MoveInLineOfSight(Unit *who) + { + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if (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) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void AttackStart(Unit* who) + { + if (who->isTargetableForAttack()) + { + DoStartAttackNoMovement(who); + + if (!InCombat) + { + Aggro(who); + InCombat = true; + } + } + } + + void Aggro(Unit *who) + { + } + + void KilledUnit(Unit *victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL_1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_1); + break; + case 1: + DoYell(SAY_KILL_2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_2); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,0); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + + /*for questId 10886 (heroic mode only) + if( pInstance && pInstance->GetData(TYPE_HARBINGERSKYRISS) != DONE ) + ->FailQuest();*/ + } + + void UpdateAI(const uint32 diff) + { + if( !Init ) + { + if( EventProgress_Timer < diff ) + { + if( Phase < 8 ) + { + switch( Phase ) + { + case 1: + DoYell(SAY_INTRO_1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_INTRO_1); + EventProgress_Timer = 18000; + break; + case 2: + DoYell(SAY_INTRO_2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_INTRO_2); + EventProgress_Timer = 18000; + break; + case 3: + DoYell(SAY_WATER,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_WATER); + DoCast(m_creature,SPELL_CONJURE_WATER); + EventProgress_Timer = 7000; + break; + case 4: + DoYell(SAY_BUFFS,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_BUFFS); + DoCast(m_creature,SPELL_ICE_ARMOR); + EventProgress_Timer = 7000; + break; + case 5: + DoYell(SAY_DRINK,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DRINK); + DoCast(m_creature,SPELL_ARCANE_INTELLECT); + EventProgress_Timer = 7000; + break; + case 6: + DoYell(SAY_READY,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_READY); + EventProgress_Timer = 6000; + break; + case 7: + if( pInstance ) + pInstance->SetData(TYPE_WARDEN_2,DONE); + Init = true; + break; + } + ++Phase; + } + } else EventProgress_Timer -= diff; + } + + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( !LowHp && ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20) ) + { + DoYell(SAY_LOWHP,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_LOWHP); + LowHp = true; + } + + if( Pyroblast_Timer < diff ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + return; + + DoYell(SAY_PYRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_PYRO); + + DoCast(m_creature->getVictim(),SPELL_PYROBLAST); + Pyroblast_Timer = 40000; + }else Pyroblast_Timer -=diff; + + if( Fireball_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_FIREBALL); + Fireball_Timer = 4000; + }else Fireball_Timer -=diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_npc_millhouse_manastorm(Creature *_Creature) +{ + return new npc_millhouse_manastormAI (_Creature); +} + +/*##### +# npc_warden_mellichar +#####*/ + +#define YELL_INTRO1 "I knew the prince would be angry but, I... I have not been myself. I had to let them out! The great one speaks to me, you see. Wait--outsiders. Kael'thas did not send you! Good... I'll just tell the prince you released the prisoners!" +#define SOUND_INTRO1 11222 + +#define YELL_INTRO2 "The naaru kept some of the most dangerous beings in existence here in these cells. Let me introduce you to another...." +#define SOUND_INTRO2 11223 + +#define YELL_RELEASE1 "Yes, yes... another! Your will is mine!" +#define SOUND_RELEASE1 11224 + +#define YELL_RELEASE2A "Behold another terrifying creature of incomprehensible power!" +#define SOUND_RELEASE2A 11225 +#define YELL_RELEASE2B "What is this? A lowly gnome? I will do better, O'great one." +#define SOUND_RELEASE2B 11226 + +#define YELL_RELEASE3 "Anarchy! Bedlam! Oh, you are so wise! Yes, I see it now, of course!" +#define SOUND_RELEASE3 11227 + +#define YELL_RELEASE4 "One final cell remains. Yes, O'great one, right away!" +#define SOUND_RELEASE4 11228 + +#define YELL_WELCOME "Welcome, O'great one. I am your humble servant." +#define SOUND_WELCOME 11229 + +//phase 2(acid mobs) +#define ENTRY_TRICKSTER 20905 +#define ENTRY_PH_HUNTER 20906 +//phase 3 +#define ENTRY_MILLHOUSE 20977 +//phase 4(acid mobs) +#define ENTRY_AKKIRIS 20908 +#define ENTRY_SULFURON 20909 +//phase 5(acid mobs) +#define ENTRY_TW_DRAK 20910 +#define ENTRY_BL_DRAK 20911 +//phase 6 +#define ENTRY_SKYRISS 20912 + +//TARGET_SCRIPT +#define SPELL_TARGET_ALPHA 36856 +#define SPELL_TARGET_BETA 36854 +#define SPELL_TARGET_DELTA 36857 +#define SPELL_TARGET_GAMMA 36858 +#define SPELL_TARGET_OMEGA 36852 +#define SPELL_BUBBLE_VISUAL 36849 + +struct MANGOS_DLL_DECL npc_warden_mellicharAI : public ScriptedAI +{ + npc_warden_mellicharAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + bool IsRunning; + bool CanSpawn; + + uint32 EventProgress_Timer; + uint32 Phase; + + void Reset() + { + IsRunning = false; + CanSpawn = false; + + EventProgress_Timer = 22000; + Phase = 1; + + m_creature->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_NON_ATTACKABLE); + DoCast(m_creature,SPELL_TARGET_OMEGA); + + if( pInstance ) + pInstance->SetData(TYPE_HARBINGERSKYRISS,NOT_STARTED); + } + + void AttackStart(Unit* who) { } + + void MoveInLineOfSight(Unit *who) + { + if( IsRunning ) + return; + + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if (m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE) + return; + if (who->GetTypeId() != TYPEID_PLAYER) + return; + + float attackRadius = m_creature->GetAttackDistance(who)/10; + if( m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who) ) + Aggro(who); + } + } + + void Aggro(Unit *who) + { + DoYell(YELL_INTRO1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_INTRO1); + //possibly wrong spell OR should also cast second spell to make bubble appear (visual for this spell appear to be the correct) + DoCast(m_creature,SPELL_BUBBLE_VISUAL); + + if( pInstance ) + { + pInstance->SetData(TYPE_HARBINGERSKYRISS,IN_PROGRESS); + IsRunning = true; + } + } + + uint32 CanProgress() + { + if( pInstance ) + { + if( Phase == 7 && pInstance->GetData(TYPE_WARDEN_4) == DONE ) + return true; + if( Phase == 6 && pInstance->GetData(TYPE_WARDEN_3) == DONE ) + return true; + if( Phase == 5 && pInstance->GetData(TYPE_WARDEN_2) == DONE ) + return true; + if( Phase == 4 ) + return true; + if( Phase == 3 && pInstance->GetData(TYPE_WARDEN_1) == DONE ) + return true; + if( Phase == 2 && pInstance->GetData(TYPE_HARBINGERSKYRISS) == IN_PROGRESS ) + return true; + if( Phase == 1 && pInstance->GetData(TYPE_HARBINGERSKYRISS) == IN_PROGRESS ) + return true; + return false; + } + return false; + } + + void DoPrepareForPhase() + { + if( pInstance ) + { + m_creature->InterruptNonMeleeSpells(true); + m_creature->RemoveSpellsCausingAura(SPELL_AURA_DUMMY); + + switch( Phase ) + { + case 2: + DoCast(m_creature,SPELL_TARGET_ALPHA); + pInstance->SetData(TYPE_WARDEN_1,IN_PROGRESS); + break; + case 3: + DoCast(m_creature,SPELL_TARGET_BETA); + pInstance->SetData(TYPE_WARDEN_2,IN_PROGRESS); + break; + case 5: + DoCast(m_creature,SPELL_TARGET_DELTA); + pInstance->SetData(TYPE_WARDEN_3,IN_PROGRESS); + break; + case 6: + DoCast(m_creature,SPELL_TARGET_GAMMA); + pInstance->SetData(TYPE_WARDEN_4,IN_PROGRESS); + break; + case 7: + pInstance->SetData(TYPE_WARDEN_5,IN_PROGRESS); + break; + default: + break; + } + CanSpawn = true; + } + } + + void UpdateAI(const uint32 diff) + { + if( !IsRunning ) + return; + + if( EventProgress_Timer < diff ) + { + if( pInstance ) + { + if( pInstance->GetData(TYPE_HARBINGERSKYRISS) == FAIL ) + Reset(); + } + + if( CanSpawn ) + { + //continue beam omega pod, unless we are about to summon skyriss + if( Phase != 7 ) + DoCast(m_creature,SPELL_TARGET_OMEGA); + + switch( Phase ) + { + case 2: + switch( rand()%2 ) + { + case 0: m_creature->SummonCreature(ENTRY_TRICKSTER,478.326,-148.505,42.56,3.19,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); break; + case 1: m_creature->SummonCreature(ENTRY_PH_HUNTER,478.326,-148.505,42.56,3.19,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); break; + } + break; + case 3: + m_creature->SummonCreature(ENTRY_MILLHOUSE,413.292,-148.378,42.56,6.27,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); + break; + case 4: + DoYell(YELL_RELEASE2B,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RELEASE2B); + break; + case 5: + switch( rand()%2 ) + { + case 0: m_creature->SummonCreature(ENTRY_AKKIRIS,420.179,-174.396,42.58,0.02,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); break; + case 1: m_creature->SummonCreature(ENTRY_SULFURON,420.179,-174.396,42.58,0.02,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); break; + } + break; + case 6: + switch( rand()%2 ) + { + case 0: m_creature->SummonCreature(ENTRY_TW_DRAK,471.795,-174.58,42.58,3.06,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); break; + case 1: m_creature->SummonCreature(ENTRY_BL_DRAK,471.795,-174.58,42.58,3.06,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); break; + } + break; + case 7: + m_creature->SummonCreature(ENTRY_SKYRISS,445.763,-191.639,44.64,1.60,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); + DoYell(YELL_WELCOME,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_WELCOME); + break; + default: + break; + } + CanSpawn = false; + ++Phase; + } + if( CanProgress() ) + { + switch( Phase ) + { + case 1: + DoYell(YELL_INTRO2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_INTRO2); + EventProgress_Timer = 10000; + ++Phase; + break; + case 2: + DoYell(YELL_RELEASE1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RELEASE1); + DoPrepareForPhase(); + EventProgress_Timer = 7000; + break; + case 3: + DoYell(YELL_RELEASE2A,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RELEASE2A); + DoPrepareForPhase(); + EventProgress_Timer = 10000; + break; + case 4: + DoPrepareForPhase(); + EventProgress_Timer = 15000; + break; + case 5: + DoYell(YELL_RELEASE3,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RELEASE3); + DoPrepareForPhase(); + EventProgress_Timer = 15000; + break; + case 6: + DoYell(YELL_RELEASE4,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_RELEASE4); + DoPrepareForPhase(); + EventProgress_Timer = 15000; + break; + case 7: + DoPrepareForPhase(); + EventProgress_Timer = 15000; + break; + default: + break; + } + } + } else EventProgress_Timer -= diff; + } +}; +CreatureAI* GetAI_npc_warden_mellichar(Creature *_Creature) +{ + return new npc_warden_mellicharAI (_Creature); +} + +/*##### +# mob_zerekethvoidzone (this script probably not needed in future -> `creature_template_addon`.`auras`='36120 0') +#####*/ + +#define SPELL_VOID_ZONE_DAMAGE 36120 + +struct MANGOS_DLL_DECL mob_zerekethvoidzoneAI : public ScriptedAI +{ + mob_zerekethvoidzoneAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() + { + m_creature->SetUInt32Value(UNIT_NPC_FLAGS,0); + m_creature->setFaction(16); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + DoCast(m_creature,SPELL_VOID_ZONE_DAMAGE); + } + + void Aggro(Unit* who) {} +}; +CreatureAI* GetAI_mob_zerekethvoidzoneAI(Creature *_Creature) +{ + return new mob_zerekethvoidzoneAI (_Creature); +} + +void AddSC_arcatraz() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_millhouse_manastorm"; + newscript->GetAI = GetAI_npc_millhouse_manastorm; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_warden_mellichar"; + newscript->GetAI = GetAI_npc_warden_mellichar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_zerekethvoidzone"; + newscript->GetAI = GetAI_mob_zerekethvoidzoneAI; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp new file mode 100644 index 00000000000..650a6a8979e --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp @@ -0,0 +1,380 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Harbinger_Skyriss +SD%Complete: 45 +SDComment: CombatAI not fully implemented. Timers will need adjustments. Need better method to "kill" the warden. Need more docs on how event fully work. Reset all event and force start over if fail at one point? +SDCategory: Tempest Keep, The Arcatraz +EndScriptData */ + +/* ContentData +boss_harbinger_skyriss +boss_harbinger_skyriss_illusion +EndContentData */ + +#include "precompiled.h" +#include "def_arcatraz.h" + +#define SAY_INTRO "It is a small matter to control the mind of the weak... for I bear allegiance to powers untouched by time, unmoved by fate. No force on this world or beyond harbors the strength to bend our knee... not even the mighty Legion!" +#define SOUND_INTRO 11122 + +#define SAY_AGGRO "Bear witness to the agent of your demise!" +#define SOUND_AGGRO 11123 + +#define SAY_KILL_1 "Your fate is written!" +#define SOUND_KILL_1 11124 +#define SAY_KILL_2 "The chaos I have sown here is but a taste...." +#define SOUND_KILL_2 11125 + +#define SAY_MIND_1 "You will do my bidding, weakling." +#define SOUND_MIND_1 11127 +#define SAY_MIND_2 "Your will is no longer your own." +#define SOUND_MIND_2 11128 + +#define SAY_FEAR_1 "Flee in terror!" +#define SOUND_FEAR_1 11129 +#define SAY_FEAR_2 "I will show you horrors undreamed of!" +#define SOUND_FEAR_2 11130 + +#define SAY_IMAGE "We span the universe, as countless as the stars!" +#define SOUND_IMAGE 11131 + +#define SAY_DEATH "I am merely one of... infinite multitudes." +#define SOUND_DEATH 11126 + +#define SPELL_FEAR 39415 + +#define SPELL_MIND_REND 36924 +#define H_SPELL_MIND_REND 39017 + +#define SPELL_DOMINATION 37162 +#define H_SPELL_DOMINATION 39019 + +#define H_SPELL_MANA_BURN 39020 + +#define SPELL_66_ILLUSION 36931 //entry 21466 +#define SPELL_33_ILLUSION 36932 //entry 21467 + +struct MANGOS_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI +{ + boss_harbinger_skyrissAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + HeroicMode = m_creature->GetMap()->IsHeroic(); + Reset(); + } + + ScriptedInstance *pInstance; + bool HeroicMode; + + bool Intro; + bool IsImage33; + bool IsImage66; + + uint32 Intro_Phase; + uint32 Intro_Timer; + uint32 MindRend_Timer; + uint32 Fear_Timer; + uint32 Domination_Timer; + uint32 ManaBurn_Timer; + + void Reset() + { + m_creature->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_UNKNOWN2); + + if( Intro ) + Intro = true; + else + Intro = false; + + IsImage33 = false; + IsImage66 = false; + + Intro_Phase = 1; + Intro_Timer = 5000; + MindRend_Timer = 3000; + Fear_Timer = 15000; + Domination_Timer = 30000; + ManaBurn_Timer = 25000; + } + + void MoveInLineOfSight(Unit *who) + { + if( !Intro ) + return; + + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if (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) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void AttackStart(Unit* who) + { + if( !Intro ) + return; + + if( who->isTargetableForAttack() ) + { + DoStartAttackAndMovement(who); + + if( !InCombat ) + { + Aggro(who); + InCombat = true; + } + } + } + + void Aggro(Unit *who) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_UNKNOWN2); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + if( pInstance ) + pInstance->SetData(TYPE_HARBINGERSKYRISS,DONE); + } + + void KilledUnit(Unit* victim) + { + /*if( victim->GetEntry() == 21436 ) + return;*/ + + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_1); + break; + case 1: + DoYell(SAY_KILL_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_2); + break; + } + } + + void JustSummoned(Creature *summoned) + { + summoned->AI()->AttackStart(m_creature->getVictim()); + } + + void DoSplit(uint32 val) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(false); + + DoYell(SAY_IMAGE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_IMAGE); + + if( val == 66 ) + DoCast(m_creature, SPELL_66_ILLUSION); + else + DoCast(m_creature, SPELL_33_ILLUSION); + } + + void UpdateAI(const uint32 diff) + { + if( !Intro && !InCombat ) + { + if( !pInstance ) + return; + + if( Intro_Timer < diff ) + { + switch( Intro_Phase ) + { + case 1: + DoYell(SAY_INTRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_INTRO); + ++Intro_Phase; + Intro_Timer = 25000; + break; + case 2: + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + if( Unit *mellic = Unit::GetUnit(*m_creature,pInstance->GetData64(DATA_MELLICHAR)) ) + { + //should have a better way to do this. possibly spell exist. + mellic->setDeathState(JUST_DIED); + mellic->SetHealth(0); + } + ++Intro_Phase; + Intro_Timer = 3000; + break; + case 3: + Intro = true; + break; + default: + break; + } + }else Intro_Timer -=diff; + } + + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( !IsImage66 && ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() <= 66) ) + { + DoSplit(66); + IsImage66 = true; + } + if( !IsImage33 && ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() <= 33) ) + { + DoSplit(33); + IsImage33 = true; + } + + if( MindRend_Timer < diff ) + { + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1) ) + DoCast(target,HeroicMode ? H_SPELL_MIND_REND : SPELL_MIND_REND); + else + DoCast(m_creature->getVictim(),HeroicMode ? H_SPELL_MIND_REND : SPELL_MIND_REND); + + MindRend_Timer = 8000; + }else MindRend_Timer -=diff; + + if( Fear_Timer < diff ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_FEAR_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_FEAR_1); + break; + case 1: + DoYell(SAY_FEAR_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_FEAR_2); + break; + } + + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1) ) + DoCast(target,SPELL_FEAR); + else + DoCast(m_creature->getVictim(),SPELL_FEAR); + + Fear_Timer = 25000; + }else Fear_Timer -=diff; + + if( Domination_Timer < diff ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_MIND_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_MIND_1); + break; + case 1: + DoYell(SAY_MIND_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_MIND_2); + break; + } + + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1) ) + DoCast(target,HeroicMode ? H_SPELL_DOMINATION : SPELL_DOMINATION); + else + DoCast(m_creature->getVictim(),HeroicMode ? H_SPELL_DOMINATION : SPELL_DOMINATION); + + Domination_Timer = 16000+rand()%16000; + }else Domination_Timer -=diff; + + if( HeroicMode ) + { + if( ManaBurn_Timer < diff ) + { + if( m_creature->IsNonMeleeSpellCasted(false) ) + return; + + if( Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1) ) + DoCast(target,H_SPELL_MANA_BURN); + + ManaBurn_Timer = 16000+rand()%16000; + }else ManaBurn_Timer -=diff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_harbinger_skyriss(Creature *_Creature) +{ + return new boss_harbinger_skyrissAI (_Creature); +} + +#define SPELL_MIND_REND_IMAGE 36929 +#define H_SPELL_MIND_REND_IMAGE 39021 + +struct MANGOS_DLL_DECL boss_harbinger_skyriss_illusionAI : public ScriptedAI +{ + boss_harbinger_skyriss_illusionAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + HeroicMode = m_creature->GetMap()->IsHeroic(); + Reset(); + } + + ScriptedInstance *pInstance; + bool HeroicMode; + + void Reset() { } + + void Aggro(Unit *who) { } +}; + +CreatureAI* GetAI_boss_harbinger_skyriss_illusion(Creature *_Creature) +{ + return new boss_harbinger_skyriss_illusionAI (_Creature); +} + +void AddSC_boss_harbinger_skyriss() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_harbinger_skyriss"; + newscript->GetAI = GetAI_boss_harbinger_skyriss; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_harbinger_skyriss_illusion"; + newscript->GetAI = GetAI_boss_harbinger_skyriss_illusion; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/def_arcatraz.h b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/def_arcatraz.h new file mode 100644 index 00000000000..8868a36d70c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/def_arcatraz.h @@ -0,0 +1,19 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ARCATRAZ_H +#define DEF_ARCATRAZ_H + +#define TYPE_ZEREKETH 1 +#define TYPE_DALLIAH 2 +#define TYPE_SOCCOTHRATES 3 +#define TYPE_HARBINGERSKYRISS 4 +#define TYPE_WARDEN_1 5 +#define TYPE_WARDEN_2 6 +#define TYPE_WARDEN_3 7 +#define TYPE_WARDEN_4 8 +#define TYPE_WARDEN_5 9 + +#define DATA_MELLICHAR 10 +#endif diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/instance_arcatraz.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/instance_arcatraz.cpp new file mode 100644 index 00000000000..7fc1c573ab6 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/instance_arcatraz.cpp @@ -0,0 +1,224 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Arcatraz +SD%Complete: 80 +SDComment: Mainly Harbringer Skyriss event +SDCategory: Tempest Keep, The Arcatraz +EndScriptData */ + +#include "precompiled.h" +#include "def_arcatraz.h" + +#define ENCOUNTERS 9 + +#define CONTAINMENT_CORE_SECURITY_FIELD_ALPHA 184318 //door opened when Wrath-Scryer Soccothrates dies +#define CONTAINMENT_CORE_SECURITY_FIELD_BETA 184319 //door opened when Dalliah the Doomsayer dies +#define POD_ALPHA 183961 //pod first boss wave +#define POD_BETA 183963 //pod second boss wave +#define POD_DELTA 183964 //pod third boss wave +#define POD_GAMMA 183962 //pod fourth boss wave +#define POD_OMEGA 183965 //pod fifth boss wave + +#define MELLICHAR 21436 //skyriss will kill this unit + +/* Arcatraz encounters: +1 - Zereketh the Unbound event +2 - Dalliah the Doomsayer event +3 - Wrath-Scryer Soccothrates event +4 - Harbinger Skyriss event, 5 sub-events +*/ + +struct MANGOS_DLL_DECL instance_arcatraz : public ScriptedInstance +{ + instance_arcatraz(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint32 Encounter[ENCOUNTERS]; + + GameObject *Containment_Core_Security_Field_Alpha; + GameObject *Containment_Core_Security_Field_Beta; + GameObject *Pod_Alpha; + GameObject *Pod_Gamma; + GameObject *Pod_Beta; + GameObject *Pod_Delta; + GameObject *Pod_Omega; + + uint64 Mellichar; + + void Initialize() + { + Containment_Core_Security_Field_Alpha = NULL; + Containment_Core_Security_Field_Beta = NULL; + Pod_Alpha = NULL; + Pod_Beta = NULL; + Pod_Delta = NULL; + Pod_Gamma = NULL; + Pod_Omega = NULL; + + Mellichar = 0; + + for(uint8 i = 0; i < ENCOUNTERS; i++) + Encounter[i] = NOT_STARTED; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < ENCOUNTERS; i++) + if(Encounter[i]) return true; + + return false; + } + + void OnObjectCreate(GameObject *go) + { + switch(go->GetEntry()) + { + case CONTAINMENT_CORE_SECURITY_FIELD_ALPHA: Containment_Core_Security_Field_Alpha = go; break; + case CONTAINMENT_CORE_SECURITY_FIELD_BETA: Containment_Core_Security_Field_Beta = go; break; + case POD_ALPHA: Pod_Alpha = go; break; + case POD_BETA: Pod_Beta = go; break; + case POD_DELTA: Pod_Delta = go; break; + case POD_GAMMA: Pod_Gamma = go; break; + case POD_OMEGA: Pod_Omega = go; break; + } + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case MELLICHAR: + Mellichar = creature->GetGUID(); + break; + } + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case TYPE_ZEREKETH: + Encounter[0] = data; + break; + + case TYPE_DALLIAH: + if( data == DONE ) + if( Containment_Core_Security_Field_Beta ) + Containment_Core_Security_Field_Beta->UseDoorOrButton(); + Encounter[1] = data; + break; + + case TYPE_SOCCOTHRATES: + if( data == DONE ) + if( Containment_Core_Security_Field_Alpha ) + Containment_Core_Security_Field_Alpha->UseDoorOrButton(); + Encounter[2] = data; + break; + + case TYPE_HARBINGERSKYRISS: + if( data == NOT_STARTED || data == FAIL ) + { + Encounter[4] = NOT_STARTED; + Encounter[5] = NOT_STARTED; + Encounter[6] = NOT_STARTED; + Encounter[7] = NOT_STARTED; + Encounter[8] = NOT_STARTED; + } + Encounter[3] = data; + break; + + case TYPE_WARDEN_1: + if( data == IN_PROGRESS ) + if( Pod_Alpha ) + Pod_Alpha->UseDoorOrButton(); + Encounter[4] = data; + break; + + case TYPE_WARDEN_2: + if( data == IN_PROGRESS ) + if( Pod_Beta ) + Pod_Beta->UseDoorOrButton(); + Encounter[5] = data; + break; + + case TYPE_WARDEN_3: + if( data == IN_PROGRESS ) + if( Pod_Delta ) + Pod_Delta->UseDoorOrButton(); + Encounter[6] = data; + break; + + case TYPE_WARDEN_4: + if( data == IN_PROGRESS ) + if( Pod_Gamma ) + Pod_Gamma->UseDoorOrButton(); + Encounter[7] = data; + break; + + case TYPE_WARDEN_5: + if( data == IN_PROGRESS ) + if( Pod_Omega ) + Pod_Omega->UseDoorOrButton(); + Encounter[8] = data; + break; + } + } + + uint32 GetData(uint32 data) + { + switch(data) + { + case TYPE_HARBINGERSKYRISS: + return Encounter[3]; + case TYPE_WARDEN_1: + return Encounter[4]; + case TYPE_WARDEN_2: + return Encounter[5]; + case TYPE_WARDEN_3: + return Encounter[6]; + case TYPE_WARDEN_4: + return Encounter[7]; + case TYPE_WARDEN_5: + return Encounter[8]; + } + return 0; + } + + uint64 GetData64(uint32 identifier) + { + switch(identifier) + { + case DATA_MELLICHAR: + return Mellichar; + } + return 0; + } +}; + +InstanceData* GetInstanceData_instance_arcatraz(Map* map) +{ + return new instance_arcatraz(map); +} + +void AddSC_instance_arcatraz() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_arcatraz"; + newscript->GetInstanceData = GetInstanceData_instance_arcatraz; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_high_botanist_freywinn.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_high_botanist_freywinn.cpp new file mode 100644 index 00000000000..3dfc0782512 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_high_botanist_freywinn.cpp @@ -0,0 +1,215 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_High_Botanist_Freywinn +SD%Complete: 90 +SDComment: some strange visual related to tree form(if aura lost before normal duration end). possible make summon&transform -process smoother(transform after delay) +SDCategory: Tempest Keep, The Botanica +EndScriptData */ + +#include "precompiled.h" + +#define SAY_AGGRO "What are you doing? These specimens are very delicate!" +#define SOUND_AGGRO 11144 + +#define SAY_KILL_1 "Your life cycle is now concluded!" +#define SOUND_KILL_1 11145 +#define SAY_KILL_2 "You will feed the worms." +#define SOUND_KILL_2 11146 + +#define SAY_TREE_1 "Endorel aluminor!" +#define SOUND_TREE_1 11147 +#define SAY_TREE_2 "Nature bends to my will!" +#define SOUND_TREE_2 11148 + +#define SAY_DEATH "The specimens...must be preserved." +#define SOUND_DEATH 11149 + +#define SPELL_TRANQUILITY 34550 +#define SPELL_TREE_FORM 34551 + +#define SPELL_SUMMON_FRAYER 34557 +#define ENTRY_FRAYER 19953 + +#define SPELL_PLANT_WHITE 34759 +#define SPELL_PLANT_GREEN 34761 +#define SPELL_PLANT_BLUE 34762 +#define SPELL_PLANT_RED 34763 + +struct MANGOS_DLL_DECL boss_high_botanist_freywinnAI : public ScriptedAI +{ + boss_high_botanist_freywinnAI(Creature *c) : ScriptedAI(c) { Reset(); } + + std::list Adds_List; + + uint32 SummonSeedling_Timer; + uint32 TreeForm_Timer; + uint32 MoveCheck_Timer; + uint32 DeadAddsCount; + bool MoveFree; + + void Reset() + { + Adds_List.clear(); + + SummonSeedling_Timer = 6000; + TreeForm_Timer = 30000; + MoveCheck_Timer = 1000; + DeadAddsCount = 0; + MoveFree = true; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void JustSummoned(Creature *summoned) + { + if( summoned->GetEntry() == ENTRY_FRAYER ) + Adds_List.push_back(summoned->GetGUID()); + } + + void DoSummonSeedling() + { + switch(rand()%4) + { + case 0: DoCast(m_creature,SPELL_PLANT_WHITE); break; + case 1: DoCast(m_creature,SPELL_PLANT_GREEN); break; + case 2: DoCast(m_creature,SPELL_PLANT_BLUE); break; + case 3: DoCast(m_creature,SPELL_PLANT_RED); break; + } + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_KILL_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_1); + break; + case 1: + DoYell(SAY_KILL_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_KILL_2); + break; + } + } + + void UpdateAI(const uint32 diff) + { + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( TreeForm_Timer < diff ) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_TREE_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_TREE_1); + break; + case 1: + DoYell(SAY_TREE_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_TREE_2); + break; + } + + if( m_creature->IsNonMeleeSpellCasted(false) ) + m_creature->InterruptNonMeleeSpells(true); + + m_creature->RemoveAllAuras(); + + DoCast(m_creature,SPELL_SUMMON_FRAYER,true); + DoCast(m_creature,SPELL_TRANQUILITY,true); + DoCast(m_creature,SPELL_TREE_FORM,true); + + m_creature->GetMotionMaster()->MoveIdle(); + MoveFree = false; + + TreeForm_Timer = 75000; + }else TreeForm_Timer -= diff; + + if( !MoveFree ) + { + if( MoveCheck_Timer < diff ) + { + if( !Adds_List.empty() ) + { + for(std::list::iterator itr = Adds_List.begin(); itr != Adds_List.end(); ++itr) + { + if( Unit *temp = Unit::GetUnit(*m_creature,*itr) ) + { + if( !temp->isAlive() ) + { + Adds_List.erase(itr); + ++DeadAddsCount; + break; + } + } + } + } + + if( DeadAddsCount < 3 && TreeForm_Timer-30000 < diff ) + DeadAddsCount = 3; + + if( DeadAddsCount >= 3 ) + { + Adds_List.clear(); + DeadAddsCount = 0; + + m_creature->InterruptNonMeleeSpells(true); + m_creature->RemoveAllAuras(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + MoveFree = true; + } + MoveCheck_Timer = 500; + } + else MoveCheck_Timer -= diff; + + return; + } + + /*if( m_creature->HasAura(SPELL_TREE_FORM,0) || m_creature->HasAura(SPELL_TRANQUILITY,0) ) + return;*/ + + //one random seedling every 5 secs, but not in tree form + if( SummonSeedling_Timer < diff ) + { + DoSummonSeedling(); + SummonSeedling_Timer = 6000; + }else SummonSeedling_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_high_botanist_freywinn(Creature *_Creature) +{ + return new boss_high_botanist_freywinnAI (_Creature); +} + +void AddSC_boss_high_botanist_freywinn() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_high_botanist_freywinn"; + newscript->GetAI = GetAI_boss_high_botanist_freywinn; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_laj.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_laj.cpp new file mode 100644 index 00000000000..f0bc53e7264 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_laj.cpp @@ -0,0 +1,198 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Laj +SD%Complete: 95 +SDComment: Immunities _may_ be applied by spells, but this has _not_ been confirmed to be proper way. Most spells require database support. +SDCategory: Tempest Keep, The Botanica +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_ALLERGIC_REACTION 34697 +#define SPELL_TELEPORT_SELF 34673 + +#define SPELL_SUMMON_LASHER_1 34681 +#define SPELL_SUMMON_FLAYER_1 34682 +#define SPELL_SUMMON_LASHER_2 34684 +#define SPELL_SUMMON_FLAYER_2 34685 +#define SPELL_SUMMON_LASHER_3 34686 +#define SPELL_SUMMON_FLAYER_4 34687 +#define SPELL_SUMMON_LASHER_4 34688 +#define SPELL_SUMMON_FLAYER_3 34690 + +#define EMOTE_SUMMON "emits a strange noise." + +#define MODEL_DEFAULT 13109 +#define MODEL_ARCANE 14213 +#define MODEL_FIRE 13110 +#define MODEL_FROST 14112 +#define MODEL_NATURE 14214 + +struct MANGOS_DLL_DECL boss_lajAI : public ScriptedAI +{ + boss_lajAI(Creature *c) : ScriptedAI(c) { Reset(); } + + bool CanSummon; + uint32 Teleport_Timer; + uint32 Summon_Timer; + uint32 Transform_Timer; + uint32 Allergic_Timer; + + void Reset() + { + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,MODEL_DEFAULT); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + + CanSummon = false; + Teleport_Timer = 20000; + Summon_Timer = 2500; + Transform_Timer = 30000; + Allergic_Timer = 5000; + } + + void DoTransform() + { + switch(rand()%5) + { + case 0: + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,MODEL_DEFAULT); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + break; + case 1: + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,MODEL_ARCANE); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + break; + case 2: + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,MODEL_FIRE); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + break; + case 3: + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,MODEL_FROST); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false); + break; + case 4: + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,MODEL_NATURE); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false); + m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true); + break; + } + } + + void DoSummons() + { + switch(rand()%4) + { + case 0: + DoCast(m_creature,SPELL_SUMMON_LASHER_1,true); + DoCast(m_creature,SPELL_SUMMON_FLAYER_1,true); + break; + case 1: + DoCast(m_creature,SPELL_SUMMON_LASHER_2,true); + DoCast(m_creature,SPELL_SUMMON_FLAYER_2,true); + break; + case 2: + DoCast(m_creature,SPELL_SUMMON_LASHER_3,true); + DoCast(m_creature,SPELL_SUMMON_FLAYER_3,true); + break; + case 3: + DoCast(m_creature,SPELL_SUMMON_LASHER_4,true); + DoCast(m_creature,SPELL_SUMMON_FLAYER_4,true); + break; + } + CanSummon = false; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( CanSummon ) + { + if( Summon_Timer < diff ) + { + DoTextEmote(EMOTE_SUMMON,NULL); + DoSummons(); + Summon_Timer = 2500; + }else Summon_Timer -= diff; + } + + if( Allergic_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_ALLERGIC_REACTION); + Allergic_Timer = 25000+rand()%15000; + }else Allergic_Timer -= diff; + + if( Teleport_Timer < diff ) + { + DoCast(m_creature,SPELL_TELEPORT_SELF); + Teleport_Timer = 30000+rand()%10000; + CanSummon = true; + }else Teleport_Timer -= diff; + + if( Transform_Timer < diff ) + { + DoTransform(); + Transform_Timer = 25000+rand()%15000; + }else Transform_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_laj(Creature *_Creature) +{ + return new boss_lajAI (_Creature); +} + +void AddSC_boss_laj() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_laj"; + newscript->GetAI = GetAI_boss_laj; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_warp_splinter.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_warp_splinter.cpp new file mode 100644 index 00000000000..ed1f6458faa --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/botanica/boss_warp_splinter.cpp @@ -0,0 +1,288 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Warp_Splinter +SD%Complete: 80 +SDComment: Includes Sapling (need some better control with these). Spells for boss possibly need some rework. +SDCategory: Tempest Keep, The Botanica +EndScriptData */ + +#include "precompiled.h" + +/*##### +# mob_treant (Sapling) +#####*/ + +struct MANGOS_DLL_DECL mob_treantAI : public ScriptedAI +{ + mob_treantAI (Creature *c) : ScriptedAI(c) + { + WarpGuid = 0; + Reset(); + } + + uint64 WarpGuid; + + void Reset() + { + m_creature->SetSpeed( MOVE_RUN, 0.5f, true); + m_creature->SetUnitMovementFlags(0); + } + + void Aggro(Unit *who) + { + return; + } + + void MoveInLineOfSight(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if (m_creature->getVictim()->GetGUID() != WarpGuid) + DoMeleeAttackIfReady(); + } +}; + +/*##### +# boss_warp_splinter +#####*/ + +#define WAR_STOMP 34716 +#define SUMMON_TREANTS 34727 // DBC: 34727, 34731, 34733, 34734, 34736, 34739, 34741 (with Ancestral Life spell 34742) // won't work (guardian summon) +#define ARCANE_VOLLEY 35059 //37078, 34785 //must additional script them (because Splinter eats them after 20 sec ^) +#define SPELL_HEAL_FATHER 6262 + +#define CREATURE_TREANT 19949 + +#define SAY_COMBAT_START "Who disturbs this sanctuary?" +#define SOUND_COMBAT_START 11230 + +#define SAY_SLAY_1 "You must die! But wait: this does not-- No, no... you must die!" +#define SOUND_SLAY_1 11231 +#define SAY_SLAY_2 "What am I doing? Why do I..." +#define SOUND_SLAY_2 11232 + +#define SAY_SUMMON_1 "Children, come to me!" +#define SOUND_SUMMON_1 11233 +#define SAY_SUMMON_2 "Maybe this is not-- No, we fight! Come to my aid." +#define SOUND_SUMMON_2 11234 + +#define SAY_DEATH "So... confused. Do not... belong here!" +#define SOUND_DEATH 11235 + +#define TREANT_SPAWN_DIST 50 //50 yards from Warp Splinter's spawn point + +float treant_pos[6][3] = +{ + {24.301233, 427.221100, -27.060635}, + {16.795492, 359.678802, -27.355425}, + {53.493484, 345.381470, -26.196192}, + {61.867096, 439.362732, -25.921030}, + {109.861877, 423.201630, -27.356019}, + {106.780159, 355.582581, -27.593357} +}; + +struct MANGOS_DLL_DECL boss_warp_splinterAI : public ScriptedAI +{ + boss_warp_splinterAI(Creature *c) : ScriptedAI(c) + { + Treant_Spawn_Pos_X = c->GetPositionX(); + Treant_Spawn_Pos_Y = c->GetPositionY(); + Reset(); + } + + uint32 War_Stomp_Timer; + uint32 Summon_Treants_Timer; + uint32 Arcane_Volley_Timer; + uint32 CheckTreantLOS_Timer; + uint32 TreantLife_Timer; + uint64 Treant_GUIDs[6]; + + float Treant_Spawn_Pos_X; + float Treant_Spawn_Pos_Y; + + + void Reset() + { + War_Stomp_Timer = 60000; + Summon_Treants_Timer = 45000; + Arcane_Volley_Timer = 140000; + CheckTreantLOS_Timer = 1000; + TreantLife_Timer = 999999; + + for(int i = 0; i < 6; ++i) + Treant_GUIDs[i] = 0; + + m_creature->SetSpeed( MOVE_RUN, 0.7f, true); + } + + void Aggro(Unit *who) + { + DoYell(SAY_COMBAT_START,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_COMBAT_START); + } + + // On Killed Unit + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY_1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY_1); + break; + case 1: + DoYell(SAY_SLAY_2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY_2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void SummonTreants() + { + for(int i = 0; i < 6; ++i) + { + float angle = (M_PI / 3) * i; + + float X = Treant_Spawn_Pos_X + TREANT_SPAWN_DIST * cos(angle); + float Y = Treant_Spawn_Pos_Y + TREANT_SPAWN_DIST * sin(angle); + //float Z = m_creature->GetMap()->GetHeight(X,Y, m_creature->GetPositionZ()); + //float Z = m_creature->GetPositionZ(); + float O = - m_creature->GetAngle(X,Y); + + Creature* pTreant = m_creature->SummonCreature(CREATURE_TREANT,treant_pos[i][0],treant_pos[i][1],treant_pos[i][2],O,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,40000); + if(pTreant) + { + //pTreant->GetMotionMaster()->Mutate(new TargetedMovementGenerator(*m_creature)); + pTreant->AddThreat(m_creature, 0.1f); + Treant_GUIDs[i] = pTreant->GetGUID(); + ((mob_treantAI*)pTreant->AI())->WarpGuid = m_creature->GetGUID(); + } + } + + switch(rand()%2) + { + case 0: + DoYell(SAY_SUMMON_1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SUMMON_1); + break; + case 1: + DoYell(SAY_SUMMON_2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SUMMON_2); + break; + } + } + + // Warp Splinter eat treants if they are near him + void EatTreant() + { + for( int i=0; i<6; ++i ) + { + Unit *pTreant = Unit::GetUnit(*m_creature, Treant_GUIDs[i]); + + if( pTreant ) + { + if( m_creature->IsWithinDistInMap(pTreant, 5)) + { + // 2) Heal Warp Splinter + int32 CurrentHP_Treant = (int32)pTreant->GetHealth(); + m_creature->CastCustomSpell(m_creature,SPELL_HEAL_FATHER,&CurrentHP_Treant, 0, 0, true,0 ,0, m_creature->GetGUID()); + + // 3) Kill Treant + pTreant->DealDamage(pTreant, pTreant->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Check for War Stomp + if(War_Stomp_Timer < diff) + { + DoCast(m_creature->getVictim(),WAR_STOMP); + War_Stomp_Timer = 60000; + } + else War_Stomp_Timer -= diff; + + //Check for Arcane Volley + if(Arcane_Volley_Timer < diff) + { + DoCast(m_creature->getVictim(),ARCANE_VOLLEY); + Arcane_Volley_Timer = 40000+((rand()%20)*1000); + } + else Arcane_Volley_Timer -= diff; + + //Check for Summon Treants + if(Summon_Treants_Timer < diff) + { + SummonTreants(); + Summon_Treants_Timer = 45000; + } + else Summon_Treants_Timer -= diff; + + // I check if there is a Treant in Warp Splinter's LOS, so he can eat them + if( CheckTreantLOS_Timer < diff) + { + EatTreant(); + CheckTreantLOS_Timer = 1000; + } + else CheckTreantLOS_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_warp_splinter(Creature *_Creature) +{ + return new boss_warp_splinterAI (_Creature); +} + +CreatureAI* GetAI_mob_treant(Creature *_Creature) +{ + return new mob_treantAI (_Creature); +} + +void AddSC_boss_warp_splinter() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_warp_splinter"; + newscript->GetAI = GetAI_boss_warp_splinter; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_warp_splinter_treant"; + newscript->GetAI = GetAI_mob_treant; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_astromancer.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_astromancer.cpp new file mode 100644 index 00000000000..c671343ad2c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_astromancer.cpp @@ -0,0 +1,563 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: boss_astromancer +SD%Complete: 75 +SDComment: +SDCategory: Tempest Keep, The Eye +EndScriptData */ + +#include "precompiled.h" +#include "def_the_eye.h" + +#define SPELL_ARCANE_MISSILES 33031 +#define SPELL_MARK_OF_THE_ASTROMANCER 33045 +#define MARK_OF_SOLARIAN 33023 +#define SPELL_BLINDING_LIGHT 33009 +#define SPELL_FEAR 29321 +#define SPELL_VOID_BOLT 39329 + +#define SAY_PHASE1 "Tal anu'men no Sin'dorei!" +#define SOUND_PHASE1 11134 +#define SAY_PHASE2 "I will crush your delusions of grandeur!" +#define SOUND_PHASE2 11140 +#define SAY_PHASE21 "Ha ha ha! You are hopelessly outmatched!" +#define SOUND_PHASE21 11139 + +#define SAY_VOID "Enough of this! Now I call upon the fury of the cosmos itself." +//#define SOUND_VOID Not found :( +#define SAY_VOID1 "I become ONE... with the VOID!" +//#define SOUND_VOID1 Not found :( + +#define SAY_KILL1 "Your soul belongs to the Abyss!" +#define SOUND_KILL1 11136 +#define SAY_KILL2 "By the blood of the Highborne!" +#define SOUND_KILL2 11137 +#define SAY_KILL3 "For the Sunwell!" +#define SOUND_KILL3 11138 +#define SAY_DEATH "The warmth of the sun... awaits." +#define SOUND_DEATH 11135 + +#define CENTER_X 432.909f +#define CENTER_Y -373.424f +#define CENTER_Z 17.9608f +#define CENTER_O 1.06421f +#define SMALL_PORTAL_RADIUS 12.6f +#define LARGE_PORTAL_RADIUS 26.0f +#define PORTAL_Z 17.005f + +#define SOLARIUM_AGENT 18925 +#define SOLARIUM_PRIEST 18806 +#define ASTROMANCER_SOLARIAN_SPOTLIGHT 18928 +#define SPELL_SPOTLIGHT 25824 +#define MODEL_HUMAN 18239 +#define MODEL_VOIDWALKER 18988 + +#define SOLARIUM_HEAL 41378 +#define SOLARIUM_SMITE 31740 +#define SOLARIUM_SILENCE 37160 + +#define WV_ARMOR 31000 +#define MIN_RANGE_FOR_DOT_JUMP 20.0f + +struct MANGOS_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI +{ + boss_high_astromancer_solarianAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + + defaultarmor=m_creature->GetArmor(); + defaultsize=m_creature->GetFloatValue(OBJECT_FIELD_SCALE_X); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 ArcaneMissiles_Timer; + uint32 MarkOfTheAstromancer_Timer; + uint32 BlindingLight_Timer; + uint32 Fear_Timer; + uint32 VoidBolt_Timer; + uint32 Phase1_Timer; + uint32 Phase2_Timer; + uint32 Phase3_Timer; + uint32 AppearDelay_Timer; + uint32 MarkOfTheSolarian_Timer; + uint32 Jump_Timer; + uint32 defaultarmor; + + float defaultsize; + + bool AppearDelay; + + uint8 Phase; + + uint64 WrathTarget; + + float Portals[3][3]; + + void Reset() + { + WrathTarget=0; + ArcaneMissiles_Timer = 2000; + MarkOfTheAstromancer_Timer = 15000; + BlindingLight_Timer = 41000; + Fear_Timer = 20000; + VoidBolt_Timer = 10000; + Phase1_Timer = 50000; + Phase2_Timer = 10000; + Phase3_Timer = 15000; + AppearDelay_Timer = 2000; + AppearDelay = false; + MarkOfTheSolarian_Timer=45000; + Jump_Timer=8000; + Phase = 1; + + if(pInstance) + pInstance->SetData(DATA_HIGHASTROMANCERSOLARIANEVENT, NOT_STARTED); + + m_creature->SetArmor(defaultarmor); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetVisibility(VISIBILITY_ON); + m_creature->SetFloatValue(OBJECT_FIELD_SCALE_X, defaultsize); + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, MODEL_HUMAN); + } + + void StartEvent() + { + DoYell(SAY_PHASE1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE1); + + if(pInstance) + pInstance->SetData(DATA_HIGHASTROMANCERSOLARIANEVENT, IN_PROGRESS); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_KILL1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL1); + break; + case 1: + DoYell(SAY_KILL2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL2); + break; + case 2: + DoYell(SAY_KILL3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_KILL3); + break; + } + } + + void JustDied(Unit *victim) + { + m_creature->SetFloatValue(OBJECT_FIELD_SCALE_X, defaultsize); + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, MODEL_HUMAN); + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if(pInstance) + pInstance->SetData(DATA_HIGHASTROMANCERSOLARIANEVENT, NOT_STARTED); + } + + void Aggro(Unit *who) + { + StartEvent(); + } + + void SummonMinion(uint32 entry, float x, float y, float z) + { + Creature* Summoned = m_creature->SummonCreature(entry, x, y, z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000); + if(Summoned) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + Summoned->AI()->AttackStart(target); + } + } + + float Portal_X(float radius) + { + if ((rand()%2)==1) radius = -radius; + return (radius * (float)(rand()%100)/100.0f + CENTER_X); + } + + float Portal_Y(float x, float radius) + { + float z; + + switch(rand()%2) + { + case 0: z = 1; break; + case 1: z = -1; break; + } + return (z*sqrt(radius*radius - (x - CENTER_X)*(x - CENTER_X)) + CENTER_Y); + } + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if (AppearDelay) + { + m_creature->StopMoving(); + m_creature->AttackStop(); + if (AppearDelay_Timer < diff) + { + AppearDelay = false; + if (Phase == 2) + { + switch (rand()%2) + { + case 0: + DoYell(SAY_PHASE2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE2); + break; + case 1: + DoYell(SAY_PHASE21, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE21); + break; + } + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetVisibility(VISIBILITY_OFF); + } + if (Phase == 3) + Phase = 1; + + AppearDelay_Timer = 2000; + }else AppearDelay_Timer -= diff; + } + + //the jumping of the dot part of the wrath of the astromancer + if(WrathTarget) + { + if(Jump_Timer< diff) + { + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + bool hasJumped = false; + + Unit* target = Unit::GetUnit((*m_creature),WrathTarget); + if(target && target->isAlive()) + { + for(std::list::iterator iter = m_threatlist.begin();iter!=m_threatlist.end();++iter) + { + Unit* currentUnit=Unit::GetUnit((*m_creature),(*iter)->getUnitGuid()); + if(currentUnit) + { + if(currentUnit->IsWithinDistInMap(target,MIN_RANGE_FOR_DOT_JUMP)&¤tUnit->isAlive()&¤tUnit!=target) + { + m_creature->CastSpell(currentUnit,SPELL_MARK_OF_THE_ASTROMANCER, false,0,0,m_creature->GetGUID()); + + Jump_Timer=8000; + WrathTarget=currentUnit->GetGUID(); + hasJumped = true; + break; + } + } + } + } + + if(!hasJumped) + WrathTarget = 0; + }else Jump_Timer -= diff; + } + + if (Phase == 1) + { + //ArcaneMissiles_Timer + if (ArcaneMissiles_Timer < diff) + { + //Solarian casts Arcane Missiles on on random targets in the raid. + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (!m_creature->HasInArc(2.5f, target)) + target = m_creature->getVictim(); + if (target) + DoCast(target, SPELL_ARCANE_MISSILES); + + ArcaneMissiles_Timer = 3000; + }else ArcaneMissiles_Timer -= diff; + + //MarkOfTheSolarian_Timer + if (MarkOfTheSolarian_Timer < diff) + { + DoCast(m_creature->getVictim(), MARK_OF_SOLARIAN); + MarkOfTheSolarian_Timer = 45000; + }else MarkOfTheSolarian_Timer -= diff; + + //MarkOfTheAstromancer_Timer + if (MarkOfTheAstromancer_Timer < diff) + { + //A debuff that lasts for 5 seconds, cast several times each phase on a random raid member, but not the main tank + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + + if(target) + { + DoCast(target, SPELL_MARK_OF_THE_ASTROMANCER); + + WrathTarget=target->GetGUID(); + Jump_Timer=8000; + } + + MarkOfTheAstromancer_Timer = 15000; + }else MarkOfTheAstromancer_Timer -= diff; + + //BlindingLight_Timer + if (BlindingLight_Timer < diff) + { + //She casts this spell every 45 seconds. It is a kind of Moonfire spell, which she strikes down on the whole raid simultaneously. It hits everyone in the raid for 2280 to 2520 arcane damage. + DoCast(m_creature->getVictim(), SPELL_BLINDING_LIGHT); + + BlindingLight_Timer = 45000; + }else BlindingLight_Timer -= diff; + + //Phase1_Timer + if (Phase1_Timer < diff) + { + Phase = 2; + Phase1_Timer = 50000; + //After these 50 seconds she portals to the middle of the room and disappears, leaving 3 light portals behind. + m_creature->GetMotionMaster()->Clear(); + m_creature->Relocate(CENTER_X, CENTER_Y, CENTER_Z, CENTER_O); + for(int i=0; i<=2; i++) + { + if (!i) + { + Portals[i][0] = Portal_X(SMALL_PORTAL_RADIUS); + Portals[i][1] = Portal_Y(Portals[i][0], SMALL_PORTAL_RADIUS); + Portals[i][2] = CENTER_Z; + }else + { + Portals[i][0] = Portal_X(LARGE_PORTAL_RADIUS); + Portals[i][1] = Portal_Y(Portals[i][0], LARGE_PORTAL_RADIUS); + Portals[i][2] = PORTAL_Z; + } + } + if((abs(Portals[2][0] - Portals[1][0]) < 7) + && (abs(Portals[2][1] - Portals[1][1]) < 7)) + { + int i=1; + if(abs(CENTER_X + 26.0f - Portals[2][0]) < 7) + i = -1; + Portals[2][0] = Portals[2][0]+7*i; + Portals[2][1] = Portal_Y(Portals[2][0], LARGE_PORTAL_RADIUS); + } + for (int i=0; i<=2; i++) + { + Creature* Summoned = m_creature->SummonCreature(ASTROMANCER_SOLARIAN_SPOTLIGHT, Portals[i][0], Portals[i][1], Portals[i][2], CENTER_O, TEMPSUMMON_TIMED_DESPAWN, Phase2_Timer+Phase3_Timer+AppearDelay_Timer+1700); + if(Summoned) + { + Summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Summoned->CastSpell(Summoned, SPELL_SPOTLIGHT, false); + } + } + AppearDelay = true; + }else Phase1_Timer-=diff; + } + else if(Phase == 2) + { + //10 seconds after Solarian disappears, 12 mobs spawn out of the three portals. + m_creature->AttackStop(); + m_creature->StopMoving(); + if (Phase2_Timer < diff) + { + Phase = 3; + for (int i=0; i<=2; i++) + for (int j=1; j<=4; j++) + SummonMinion(SOLARIUM_AGENT, Portals[i][0], Portals[i][1], Portals[i][2]); + Phase2_Timer = 10000; + } else Phase2_Timer -= diff; + } + else if(Phase == 3) + { + //Check Phase3_Timer + if(Phase3_Timer < diff) + { + //15 seconds later Solarian reappears out of one of the 3 portals. Simultaneously, 2 healers appear in the two other portals. + m_creature->AttackStop(); + m_creature->StopMoving(); + int i = rand()%3; + m_creature->GetMotionMaster()->Clear(); + m_creature->Relocate(Portals[i][0], Portals[i][1], Portals[i][2], CENTER_O); + + for (int j=0; j<=2; j++) + if (j!=i) + SummonMinion(SOLARIUM_PRIEST, Portals[j][0], Portals[j][1], Portals[j][2]); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetVisibility(VISIBILITY_ON); + + DoYell(SAY_PHASE1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE1); + AppearDelay = true; + Phase3_Timer = 15000; + }else Phase3_Timer -= diff; + } + else if(Phase == 4) + { + //Fear_Timer + if (Fear_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_FEAR); + Fear_Timer = 20000; + }else Fear_Timer -= diff; + + //VoidBolt_Timer + if (VoidBolt_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_VOID_BOLT); + VoidBolt_Timer = 10000; + }else VoidBolt_Timer -= diff; + } + + //When Solarian reaches 20% she will transform into a huge void walker. + if(Phase != 4 && ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth())<20)) + { + Phase = 4; + + //To make sure she wont be invisible or not selecatble + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetVisibility(VISIBILITY_ON); + switch(rand()%2) + { + case 0: + DoYell(SAY_VOID, LANG_UNIVERSAL, NULL); + break; + case 1: + DoYell(SAY_VOID1, LANG_UNIVERSAL, NULL); + break; + } + + m_creature->SetArmor(WV_ARMOR); + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, MODEL_VOIDWALKER); + m_creature->SetFloatValue(OBJECT_FIELD_SCALE_X, defaultsize*2.5f); + } + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL mob_solarium_priestAI : public ScriptedAI +{ + mob_solarium_priestAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 healTimer; + uint32 holysmiteTimer; + uint32 aoesilenceTimer; + + void Reset() + { + healTimer = 9000; + holysmiteTimer = 1; + aoesilenceTimer = 15000; + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (healTimer < diff) + { + Unit* target = NULL; + + switch(rand()%2) + { + case 0: + if(pInstance) + target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_ASTROMANCER)); + break; + case 1: + target = m_creature; + break; + } + + if(target) + { + DoCast(target,SOLARIUM_HEAL); + healTimer = 9000; + } + } else healTimer -= diff; + + if(holysmiteTimer < diff) + { + DoCast(m_creature->getVictim(), SOLARIUM_SMITE); + holysmiteTimer = 4000; + } else holysmiteTimer -= diff; + + if (aoesilenceTimer < diff) + { + DoCast(m_creature->getVictim(), SOLARIUM_SILENCE); + aoesilenceTimer = 13000; + } else aoesilenceTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_solarium_priest(Creature *_Creature) +{ + return new mob_solarium_priestAI (_Creature); +} + +CreatureAI* GetAI_boss_high_astromancer_solarian(Creature *_Creature) +{ + return new boss_high_astromancer_solarianAI (_Creature); +} + +void AddSC_boss_high_astromancer_solarian() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_high_astromancer_solarian"; + newscript->GetAI = GetAI_boss_high_astromancer_solarian; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_solarium_priest"; + newscript->GetAI = GetAI_mob_solarium_priest; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_kaelthas.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_kaelthas.cpp new file mode 100644 index 00000000000..4e88e44f799 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_kaelthas.cpp @@ -0,0 +1,1639 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: boss_Kaelthas +SD%Complete: 60 +SDComment: SQL, phase 2, phase 3, Mind Control, taunt immunity +SDCategory: Tempest Keep, The Eye +EndScriptData */ + +#include "precompiled.h" +#include "def_the_eye.h" +#include "WorldPacket.h" + +//Phase 2 spells (Not used) +#define SPELL_SUMMON_WEAPONS 36976 +#define SPELL_SUMMON_WEAPONA 36958 +#define SPELL_SUMMON_WEAPONB 36959 +#define SPELL_SUMMON_WEAPONC 36960 +#define SPELL_SUMMON_WEAPOND 36961 +#define SPELL_SUMMON_WEAPONE 36962 +#define SPELL_SUMMON_WEAPONF 36963 +#define SPELL_SUMMON_WEAPONG 36964 +#define SPELL_RES_VISUAL 24171 +#define SPELL_WEAPON_SPAWN 41236 +//Phase 4 spells +#define SPELL_FIREBALL 22088 //wrong but works with CastCustomSpell +#define SPELL_PYROBLAST 36819 +#define SPELL_FLAME_STRIKE 36735 +#define SPELL_FLAME_STRIKE_VIS 36730 +#define SPELL_FLAME_STRIKE_DMG 36731 +#define SPELL_ARCANE_DISRUPTION 36834 +#define SPELL_SHOCK_BARRIER 36815 +#define SPELL_PHOENIX_ANIMATION 36723 +#define SPELL_MIND_CONTROL 32830 +//Phase 5 spells +#define SPELL_EXPLODE 36092 +#define SPELL_FULLPOWER 36187 +#define SPELL_KNOCKBACK 11027 +#define SPELL_GRAVITY_LAPSE 34480 +#define SPELL_GRAVITY_LAPSE_AURA 39432 +#define SPELL_NETHER_BEAM 35873 +//Thaladred the Darkener spells +#define SPELL_PSYCHIC_BLOW 10689 +#define SPELL_SILENCE 30225 +//Lord Sanguinar spells +#define SPELL_BELLOWING_ROAR 40636 +//Grand Astromancer Capernian spells +#define CAPERNIAN_DISTANCE 20 //she casts away from the target +#define SPELL_CAPERNIAN_FIREBALL 36971 +#define SPELL_CONFLAGRATION 37018 +#define SPELL_ARCANE_EXPLOSION 36970 +//Master Engineer Telonicus spells +#define SPELL_BOMB 37036 +#define SPELL_REMOTE_TOY 37027 +//Nether Vapor spell +#define SPELL_NETHER_VAPOR 35859 +//Phoenix spell +#define SPELL_BURN 36721 + +//kael'thas Speech +#define SAY_INTRO "Energy. Power. My people are addicted to it... a dependence made manifest after the Sunwell was destroyed. Welcome... to the future. A pity you are too late to stop it. No one can stop me now! Selama ashal'anore!" +#define SOUND_INTRO 11256 + +#define SAY_ASTROMANCER_CAPERNIAN "Capernian will see to it that your stay here is a short one." +#define SOUND_ASTROMANCER_CAPERNIAN 11257 + +#define SAY_ENGINEER_TELONICUS "Well done, you have proven worthy to test your skills against my master engineer, Telonicus." +#define SOUND_ENGINEER_TELONICUS 11258 + +#define SAY_THALADRED_THE_DARKENER "Let us see how your nerves hold up against the Darkener, Thaladred" +#define SOUND_THALADRED_THE_DARKENER 11259 + +#define SAY_LORD_SANGUINAR "You have persevered against some of my best advisors... but none can withstand the might of the Blood Hammer. Behold, Lord Sanguinar!" +#define SOUND_LORD_SANGUINAR 11260 + +#define SAY_PHASE2 "As you see, I have many weapons in my arsenal...." +#define SOUND_PHASE2 11261 + +#define SAY_PHASE3 "Perhaps I underestimated you. It would be unfair to make you fight all four advisors at once, but... fair treatment was never shown to my people. I'm just returning the favor." +#define SOUND_PHASE3 11262 + +#define SAY_PHASE4 "Alas, sometimes one must take matters into one's own hands. Balamore shanal!" +#define SOUND_PHASE4 11263 + +#define SAY_PHASE5 "I have not come this far to be stopped! The future I have planned will not be jeopardized! Now you will taste true power!!" +#define SOUND_PHASE5 11273 + +#define SAY_SLAY1 "You will not prevail." +#define SOUND_SLAY1 11270 + +#define SAY_SLAY2 "You gambled...and lost." +#define SOUND_SLAY2 11271 + +#define SAY_MINDCONTROL1 "Obey me." +#define SOUND_MINDCONTROL1 11268 + +#define SAY_MINDCONTROL2 "Bow to my will." +#define SOUND_MINDCONTROL2 11269 + +#define SAY_GRAVITYLAPSE1 "Let us see how you fare when your world is turned upside down." +#define SOUND_GRAVITYLAPSE1 11264 + +#define SAY_GRAVITYLAPSE2 "Having trouble staying grounded?" +#define SOUND_GRAVITYLAPSE2 11265 + +#define SAY_SUMMON_PHOENIX1 "Anara'nel belore!" +#define SOUND_SUMMON_PHOENIX1 11267 + +#define SAY_SUMMON_PHOENIX2 "By the power of the sun!" +#define SOUND_SUMMON_PHOENIX2 11266 + +#define SAY_DEATH "For...Quel...thalas!" +#define SOUND_DEATH 11274 + +//Thaladred the Darkener speech +#define SAY_THALADRED_AGGRO "Prepare yourselves!" +#define SOUND_THALADRED_AGGRO 11203 +#define SAY_THALADRED_DEATH "Forgive me, my prince! I have... failed." +#define SOUND_THALADRED_DEATH 11204 +#define EMOTE_THALADRED_GAZE "sets his gaze on $N!" + +//Lord Sanguinar speech +#define SAY_SANGUINAR_AGGRO "Blood for blood!" +#define SOUND_SANGUINAR_AGGRO 11152 +#define SAY_SANGUINAR_DEATH "NO! I ...will... not..." +#define SOUND_SANGUINAR_DEATH 11153 + +//Grand Astromancer Capernian speech +#define SAY_CAPERNIAN_AGGRO "The sin'dore reign supreme!" +#define SOUND_CAPERNIAN_AGGRO 11117 +#define SAY_CAPERNIAN_DEATH "This is not over!" +#define SOUND_CAPERNIAN_DEATH 11118 + +//Master Engineer Telonicus speech +#define SAY_TELONICUS_AGGRO "Anar'alah belore!" +#define SOUND_TELONICUS_AGGRO 11157 +#define SAY_TELONICUS_DEATH "More perils... await" +#define SOUND_TELONICUS_DEATH 11158 + +//Creature IDs +#define PHOENIX 21362 +#define PHOENIX_EGG 21364 + +//Phoenix egg and phoenix model +#define PHOENIX_MODEL 19682 +#define PHOENIX_EGG_MODEL 20245 + +//#define PI 3.141592 +#define TEMP_MC_WHISPER "[SD2 Debug] You would be mind controlled here!" + +//weapon id + position +float KaelthasWeapons[7][5] = +{ + {21270, 794.38, 15, 48.72, 2.9}, //[Cosmic Infuser] + {21269, 785.47, 12.12, 48.72, 3.14}, //[Devastation] + {21271, 781.25, 4.39, 48.72, 3.14}, //[Infinity Blade] + {21273, 777.38, -0.81, 48.72, 3.06}, //[Phaseshift Bulwark] + {21274, 781.48, -6.08, 48.72, 3.9}, //[Staff of Disintegration] + {21272, 785.42, -13.59, 48.72, 3.4}, //[Warp Slicer] + {21268, 793.06, -16.61, 48.72, 3.10} //[Netherstrand Longbow] +}; + +#define GRAVITY_X 795.0f +#define GRAVITY_Y 0.0f +#define GRAVITY_Z 70.0f + +#define TIME_PHASE_2_3 120000 +#define TIME_PHASE_3_4 120000 + +#define KAEL_VISIBLE_RANGE 50.0f + +//Base AI for Advisors +struct MANGOS_DLL_DECL advisorbase_ai : public ScriptedAI +{ + ScriptedInstance* pInstance; + bool FakeDeath; + uint32 DelayRes_Timer; + uint64 DelayRes_Target; + + advisorbase_ai(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || FakeDeath || m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + if( !m_creature->getVictim() && who->isTargetableForAttack() && ( m_creature->IsHostileTo( who )) && who->isInAccessablePlaceFor(m_creature) ) + { + if (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) + { + Aggro(who); + InCombat = true; + } + } + } + } + + void AttackStart(Unit* who) + { + if (!who || FakeDeath || m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + if (who->isTargetableForAttack()) + { + //Begin attack + DoStartAttackAndMovement(who); + + if (!InCombat) + { + Aggro(who); + InCombat = true; + } + } + } + + void Reset() + { + FakeDeath = false; + DelayRes_Timer = 0; + DelayRes_Target = 0; + + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + //reset encounter + if(pInstance && (pInstance->GetData(DATA_KAELTHASEVENT) == 1 || pInstance->GetData(DATA_KAELTHASEVENT) == 3)) + { + Creature *Kaelthas = NULL; + Kaelthas = (Creature*)(Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KAELTHAS))); + + if(Kaelthas) + Kaelthas->AI()->EnterEvadeMode(); + } + } + + void Revive(Unit* Target) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetHealth(m_creature->GetMaxHealth()); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + DoCast(m_creature, SPELL_RES_VISUAL, false); + DelayRes_Timer = 2000; + } + + void DamageTaken(Unit* pKiller, uint32 &damage) + { + if (damage < m_creature->GetHealth()) + return; + + //Prevent glitch if in fake death + if (FakeDeath) + { + damage = 0; + return; + } + //Don't really die in phase 1 & 3, only die after that + if(pInstance && pInstance->GetData(DATA_KAELTHASEVENT) != 0) + { + //prevent death + damage = 0; + FakeDeath = true; + + m_creature->InterruptNonMeleeSpells(false); + m_creature->SetHealth(0); + m_creature->StopMoving(); + m_creature->ClearComboPointHolders(); + m_creature->RemoveAllAurasOnDeath(); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false); + m_creature->ModifyAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, false); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->ClearAllReactives(); + m_creature->SetUInt64Value(UNIT_FIELD_TARGET,0); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,PLAYER_STATE_DEAD); + + if (pInstance->GetData(DATA_KAELTHASEVENT) == 3) + JustDied(pKiller); + } + } + + void UpdateAI(const uint32 diff) + { + if (DelayRes_Timer) + if (DelayRes_Timer <= diff) + { + DelayRes_Timer = 0; + FakeDeath = false; + + Unit* Target = Unit::GetUnit((*m_creature), DelayRes_Target); + if (!Target)Target = m_creature->getVictim(); + DoResetThreat(); + AttackStart(Target); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(Target); + m_creature->AddThreat(Target, 0.0f); + }else DelayRes_Timer -= diff; + } + +}; + +//Kael'thas AI +struct MANGOS_DLL_DECL boss_kaelthasAI : public ScriptedAI +{ + boss_kaelthasAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + AdvisorGuid[0] = 0; + AdvisorGuid[1] = 0; + AdvisorGuid[2] = 0; + AdvisorGuid[3] = 0; + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 Fireball_Timer; + uint32 ArcaneDisruption_Timer; + uint32 Phoenix_Timer; + uint32 ShockBarrier_Timer; + uint32 GravityLapse_Timer; + uint32 GravityLapse_Phase; + uint32 NetherBeam_Timer; + uint32 NetherVapor_Timer; + uint32 FlameStrike_Timer; + uint32 MindControl_Timer; + uint32 Phase; + uint32 PhaseSubphase; //generic + uint32 Phase_Timer; //generic timer + uint32 PyrosCasted; + + bool InGravityLapse; + bool IsCastingFireball; + bool ChainPyros; + + uint64 AdvisorGuid[4]; + + void Reset() + { + Fireball_Timer = 5000+rand()%10000; + ArcaneDisruption_Timer = 45000; + MindControl_Timer = 40000; + Phoenix_Timer = 50000; + ShockBarrier_Timer = 60000; + FlameStrike_Timer = 30000; + GravityLapse_Timer = 20000; + GravityLapse_Phase = 0; + NetherBeam_Timer = 8000; + NetherVapor_Timer = 10000; + PyrosCasted = 0; + Phase = 0; + InGravityLapse = false; + IsCastingFireball = false; + ChainPyros = false; + + if(InCombat) + PrepareAdvisors(); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + if(pInstance) + pInstance->SetData(DATA_KAELTHASEVENT, 0); + } + + void PrepareAdvisors() + { + Creature *pCreature; + for(uint8 i = 0; i < 4; i++) + { + pCreature = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[i])); + if(pCreature) + { + pCreature->Respawn(); + pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + pCreature->setFaction(m_creature->getFaction()); + pCreature->AI()->EnterEvadeMode(); + } + } + } + + void StartEvent() + { + if(!pInstance) + return; + + AdvisorGuid[0] = pInstance->GetData64(DATA_THALADREDTHEDARKENER); + AdvisorGuid[1] = pInstance->GetData64(DATA_LORDSANGUINAR); + AdvisorGuid[2] = pInstance->GetData64(DATA_GRANDASTROMANCERCAPERNIAN); + AdvisorGuid[3] = pInstance->GetData64(DATA_MASTERENGINEERTELONICUS); + + if(!AdvisorGuid[0] || !AdvisorGuid[1] || !AdvisorGuid[2] || !AdvisorGuid[3]) + { + error_log("SD2: Kael'Thas One or more advisors missing, Skipping Phases 1-3"); + DoYell("SD2: Kael'Thas One or more advisors missing, Skipping Phases 1-3", LANG_UNIVERSAL, NULL); + + DoYell(SAY_PHASE4, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE4); + Phase = 4; + + pInstance->SetData(DATA_KAELTHASEVENT, 4); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + DoStartAttackAndMovement(target); + } + }else + { + PrepareAdvisors(); + + DoYell(SAY_INTRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_INTRO); + + pInstance->SetData(DATA_KAELTHASEVENT, 1); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + PhaseSubphase = 0; + Phase_Timer = 23000; + Phase = 1; + } + } + + void KilledUnit() + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY2); + break; + } + } + + void JustDied(Unit* Killer) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + + if(pInstance) + pInstance->SetData(DATA_KAELTHASEVENT, 0); + + Creature *pCreature; + for(uint8 i = 0; i < 4; i++) + { + pCreature = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[i])); + if(pCreature) + { + pCreature->DealDamage(pCreature, pCreature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + } + } + } + + void Aggro(Unit *who) + { + if (pInstance && !pInstance->GetData(DATA_KAELTHASEVENT) && !Phase) + StartEvent(); + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (Phase >= 4 && m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + } + else if(who->isAlive()) + { + if (pInstance && !pInstance->GetData(DATA_KAELTHASEVENT) && !Phase && m_creature->IsWithinDistInMap(who, 60.0f)) + StartEvent(); + + //add to the threat list, so we can use SelectTarget + m_creature->AddThreat(who,0.0f); + } + } + } + + void UpdateAI(const uint32 diff) + { + //Phase 1 + switch (Phase) + { + case 1: + { + Unit *target; + Creature* Advisor; + + //Subphase switch + switch(PhaseSubphase) + { + //Subphase 1 - Start + case 0: + if(Phase_Timer < diff) + { + DoYell(SAY_THALADRED_THE_DARKENER, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_THALADRED_THE_DARKENER); + + //start advisor within 7 seconds + Phase_Timer = 7000; + + PhaseSubphase++; + }else Phase_Timer -= diff; + break; + + //Subphase 1 - Unlock advisor + case 1: + if(Phase_Timer < diff) + { + Advisor = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[0])); + + if(Advisor) + { + Advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + Advisor->setFaction(m_creature->getFaction()); + + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + Advisor->AI()->AttackStart(target); + } + + PhaseSubphase++; + }else Phase_Timer -= diff; + break; + + //Subphase 2 - Start + case 2: + Advisor = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[0])); + if(Advisor && (Advisor->GetUInt32Value(UNIT_FIELD_BYTES_1) == PLAYER_STATE_DEAD)) + { + DoYell(SAY_LORD_SANGUINAR, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_LORD_SANGUINAR); + + //start advisor within 12.5 seconds + Phase_Timer = 12500; + + PhaseSubphase++; + } + break; + + //Subphase 2 - Unlock advisor + case 3: + if(Phase_Timer < diff) + { + Advisor = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[1])); + + if(Advisor) + { + Advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + Advisor->setFaction(m_creature->getFaction()); + + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + Advisor->AI()->AttackStart(target); + } + + PhaseSubphase++; + }else Phase_Timer -= diff; + break; + + //Subphase 3 - Start + case 4: + Advisor = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[1])); + if(Advisor && (Advisor->GetUInt32Value(UNIT_FIELD_BYTES_1) == PLAYER_STATE_DEAD)) + { + DoYell(SAY_ASTROMANCER_CAPERNIAN, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ASTROMANCER_CAPERNIAN); + + //start advisor within 7 seconds + Phase_Timer = 7000; + + PhaseSubphase++; + } + break; + + //Subphase 3 - Unlock advisor + case 5: + if(Phase_Timer < diff) + { + Advisor = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[2])); + + if(Advisor) + { + Advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + Advisor->setFaction(m_creature->getFaction()); + + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + Advisor->AI()->AttackStart(target); + } + + PhaseSubphase++; + }else Phase_Timer -= diff; + break; + + //Subphase 4 - Start + case 6: + Advisor = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[2])); + if(Advisor && (Advisor->GetUInt32Value(UNIT_FIELD_BYTES_1) == PLAYER_STATE_DEAD)) + { + DoYell(SAY_ENGINEER_TELONICUS, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_ENGINEER_TELONICUS); + + //start advisor within 8.4 seconds + Phase_Timer = 8400; + + PhaseSubphase++; + } + break; + + //Subphase 4 - Unlock advisor + case 7: + if(Phase_Timer < diff) + { + Advisor = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[3])); + + if(Advisor) + { + Advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + Advisor->setFaction(m_creature->getFaction()); + + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + Advisor->AI()->AttackStart(target); + } + + Phase_Timer = 3000; + + PhaseSubphase++; + }else Phase_Timer -= diff; + break; + + //End of phase 1 + case 8: + Advisor = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[3])); + if(Advisor && (Advisor->GetUInt32Value(UNIT_FIELD_BYTES_1) == PLAYER_STATE_DEAD)) + { + Phase = 2; + pInstance->SetData(DATA_KAELTHASEVENT, 2); + + DoYell(SAY_PHASE2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE2); + PhaseSubphase = 0; + Phase_Timer = 3500; + DoCast(m_creature, SPELL_SUMMON_WEAPONS); + } + break; + } + }break; + + case 2: + { + if (PhaseSubphase == 0) + { + if (Phase_Timer < diff) + { + PhaseSubphase = 1; + }else Phase_Timer -= diff; + } + + //Spawn weapons + if (PhaseSubphase == 1) + { + Unit* Target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + Creature* Weapon; + for (uint32 i = 0; i < 7; i++) + { + Weapon = m_creature->SummonCreature(((uint32)KaelthasWeapons[i][0]),KaelthasWeapons[i][1],KaelthasWeapons[i][2],KaelthasWeapons[i][3],0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000); + + if (!Weapon) + error_log("SD2: Kael'thas weapon %i could not be spawned", i); + else + { + Weapon->setFaction(m_creature->getFaction()); + Weapon->AI()->AttackStart(Target); + Weapon->CastSpell(Weapon, SPELL_WEAPON_SPAWN, false); + } + } + + PhaseSubphase = 2; + Phase_Timer = TIME_PHASE_2_3; + } + + if (PhaseSubphase == 2) + if (Phase_Timer < diff) + { + DoYell(SAY_PHASE3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE3); + pInstance->SetData(DATA_KAELTHASEVENT, 3); + Phase = 3; + PhaseSubphase = 0; + }else Phase_Timer -= diff; + }break; + + case 3: + { + if (PhaseSubphase == 0) + { + //Respawn advisors + Unit* Target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + Creature* Advisor; + for (uint32 i = 0; i < 4; i++) + { + Advisor = (Creature*)(Unit::GetUnit((*m_creature), AdvisorGuid[i])); + if (!Advisor) + error_log("SD2: Kael'Thas Advisor %u does not exist. Possibly despawned? Incorrectly Killed?", i); + else ((advisorbase_ai*)Advisor->AI())->Revive(Target); + } + + PhaseSubphase = 1; + Phase_Timer = TIME_PHASE_3_4; + } + + if(Phase_Timer < diff) + { + DoYell(SAY_PHASE4, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE4); + Phase = 4; + + pInstance->SetData(DATA_KAELTHASEVENT, 4); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + DoStartAttackAndMovement(target); + } + Phase_Timer = 30000; + }else Phase_Timer -= diff; + } + break; + + case 4: + case 5: + case 6: + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Fireball_Timer + if(!InGravityLapse && !ChainPyros && Phase != 5) + { + if(Fireball_Timer < diff) + { + if(!IsCastingFireball) + { + if(!m_creature->IsNonMeleeSpellCasted(false)) + { + //interruptable + m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, false); + int32 dmg = 20000+rand()%5000; + m_creature->CastCustomSpell(m_creature->getVictim(), SPELL_FIREBALL, &dmg, 0, 0, false); + IsCastingFireball = true; + Fireball_Timer = 2500; + } + } + else + { + //apply resistance + m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_INTERRUPT_CAST, true); + IsCastingFireball = false; + Fireball_Timer = 5000+rand()%10000; + } + }else Fireball_Timer -= diff; + + //ArcaneDisruption_Timer + if(ArcaneDisruption_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ARCANE_DISRUPTION, true); + + ArcaneDisruption_Timer = 60000; + }else ArcaneDisruption_Timer -= diff; + + if (FlameStrike_Timer < diff) + { + Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0); + DoCast(pUnit, SPELL_FLAME_STRIKE); + + FlameStrike_Timer = 30000; + }FlameStrike_Timer -= diff; + + if (MindControl_Timer < diff) + { + if (m_creature->getThreatManager().getThreatList().size() >= 2) + for (uint32 i = 0; i < 3; i++) + { + debug_log("SD2: Kael'Thas mind control not supported."); + //DoCast(pUnit, SPELL_MIND_CONTROL); + } + + MindControl_Timer = 60000; + }MindControl_Timer -= diff; + } + + //Phoenix_Timer + if(Phoenix_Timer < diff) + { + DoCast(m_creature, SPELL_PHOENIX_ANIMATION); + Creature *Phoenix = DoSpawnCreature(PHOENIX, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000); + + if(Phoenix) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (target) + Phoenix->AI()->AttackStart(target); + }else error_log("SD2: Kael'Thas Phoenix could not be spawned"); + + switch(rand()%2) + { + case 0: + DoYell(SAY_SUMMON_PHOENIX1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SUMMON_PHOENIX1); + break; + + case 1: + DoYell(SAY_SUMMON_PHOENIX2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SUMMON_PHOENIX2); + break; + + default: + break; + } + + Phoenix_Timer = 60000; + }else Phoenix_Timer -= diff; + + //Phase 4 specific spells + if(Phase == 4) + { + if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 50) + { + pInstance->SetData(DATA_KAELTHASEVENT, 4); + Phase = 5; + Phase_Timer = 10000; + + DoYell(SAY_PHASE5, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_PHASE5); + + m_creature->StopMoving(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->Relocate(GRAVITY_X, GRAVITY_Y, GRAVITY_Z, 0); + m_creature->SendMonsterMove(GRAVITY_X, GRAVITY_Y,GRAVITY_Z, 0, 0, 0); + + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature, SPELL_FULLPOWER); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + } + + //ShockBarrier_Timer + if(ShockBarrier_Timer < diff) + { + DoCast(m_creature, SPELL_SHOCK_BARRIER); + ChainPyros = true; + PyrosCasted = 0; + + ShockBarrier_Timer = 60000; + }else ShockBarrier_Timer -= diff; + + //Chain Pyros (3 of them max) + if (ChainPyros && !m_creature->IsNonMeleeSpellCasted(false)) + { + if (PyrosCasted < 3) + { + DoCast(m_creature->getVictim(), SPELL_PYROBLAST); + PyrosCasted++; + + }else + { + ChainPyros = false; + Fireball_Timer = 2500; + ArcaneDisruption_Timer = 60000; + } + } + } + + if (Phase == 5) + { + if(Phase_Timer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + m_creature->RemoveAurasDueToSpell(SPELL_FULLPOWER); + DoCast(m_creature, SPELL_EXPLODE); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + Phase = 6; + DoStartAttackAndMovement(m_creature->getVictim()); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + + }else Phase_Timer -= diff; + } + + //Phase 5 + if(Phase == 6) + { + + //GravityLapse_Timer + if(GravityLapse_Timer < diff) + { + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + switch(GravityLapse_Phase) + { + case 0: + m_creature->StopMoving(); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->Relocate(GRAVITY_X, GRAVITY_Y, GRAVITY_Z, 0); + m_creature->SendMonsterMove(GRAVITY_X, GRAVITY_Y, GRAVITY_Z, 0, 0, 0); + // 1) Kael'thas will portal the whole raid right into his body + for (i = m_creature->getThreatManager().getThreatList().begin(); i!= m_creature->getThreatManager().getThreatList().end();++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit && (pUnit->GetTypeId() == TYPEID_PLAYER)) + { + //Use work around packet to prevent player from being dropped from combat + DoTeleportPlayer(pUnit, GRAVITY_X, GRAVITY_Y, GRAVITY_Z, pUnit->GetOrientation()); + } + } + GravityLapse_Timer = 500; + ++GravityLapse_Phase; + InGravityLapse = true; + ShockBarrier_Timer = 1000; + NetherBeam_Timer = 5000; + break; + + case 1: + switch(rand()%2) + { + case 0: + DoYell(SAY_GRAVITYLAPSE1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_GRAVITYLAPSE1); + break; + + case 1: + DoYell(SAY_GRAVITYLAPSE2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_GRAVITYLAPSE2); + break; + } + + // 2) At that point he will put a Gravity Lapse debuff on everyone + for (i = m_creature->getThreatManager().getThreatList().begin(); i!= m_creature->getThreatManager().getThreatList().end();i++) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit) + { + m_creature->CastSpell(pUnit, SPELL_KNOCKBACK, true); + //Gravity lapse - needs an exception in Spell system to work + + pUnit->CastSpell(pUnit, SPELL_GRAVITY_LAPSE, true, 0, 0, m_creature->GetGUID()); + pUnit->CastSpell(pUnit, SPELL_GRAVITY_LAPSE_AURA, true, 0, 0, m_creature->GetGUID()); + + //Using packet workaround + WorldPacket data(12); + data.SetOpcode(SMSG_MOVE_SET_CAN_FLY); + data.append(pUnit->GetPackGUID()); + data << uint32(0); + pUnit->SendMessageToSet(&data, true); + } + } + GravityLapse_Timer = 10000; + GravityLapse_Phase++; + break; + + case 2: + //Cast nether vapor aura on self + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature, SPELL_NETHER_VAPOR); + + GravityLapse_Timer = 20000; + GravityLapse_Phase++; + break; + + case 3: + //Remove flight + for (i = m_creature->getThreatManager().getThreatList().begin(); i!= m_creature->getThreatManager().getThreatList().end();i++) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if(pUnit) + { + //Using packet workaround + WorldPacket data(12); + data.SetOpcode(SMSG_MOVE_UNSET_CAN_FLY); + data.append(pUnit->GetPackGUID()); + data << uint32(0); + pUnit->SendMessageToSet(&data, true); + } + } + m_creature->RemoveAurasDueToSpell(SPELL_NETHER_VAPOR); + InGravityLapse = false; + GravityLapse_Timer = 60000; + GravityLapse_Phase = 0; + DoStartAttackAndMovement(m_creature->getVictim()); + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim()); + DoResetThreat(); + break; + } + }else GravityLapse_Timer -= diff; + + if(InGravityLapse) + { + //ShockBarrier_Timer + if(ShockBarrier_Timer < diff) + { + DoCast(m_creature, SPELL_SHOCK_BARRIER); + + ShockBarrier_Timer = 20000; + }else ShockBarrier_Timer -= diff; + + //NetherBeam_Timer + if(NetherBeam_Timer < diff) + { + Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0); + DoCast(pUnit, SPELL_NETHER_BEAM); + + NetherBeam_Timer = 4000; + }else NetherBeam_Timer -= diff; + } + } + + if (!InGravityLapse) + DoMeleeAttackIfReady(); + } + } + } +}; + +//Thaladred the Darkener AI +struct MANGOS_DLL_DECL boss_thaladred_the_darkenerAI : public advisorbase_ai +{ + boss_thaladred_the_darkenerAI(Creature *c) : advisorbase_ai(c) {} + + uint32 Gaze_Timer; + uint32 Silence_Timer; + uint32 PsychicBlow_Timer; + + void Reset() + { + Gaze_Timer = 100; + Silence_Timer = 20000; + PsychicBlow_Timer = 10000; + + advisorbase_ai::Reset(); + } + + void JustDied(Unit* pKiller) + { + DoPlaySoundToSet(m_creature, SOUND_THALADRED_DEATH); + DoYell(SAY_THALADRED_DEATH, LANG_UNIVERSAL, NULL); + } + + void Aggro(Unit *who) + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + if (!who || FakeDeath) + return; + + DoYell(SAY_THALADRED_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_THALADRED_AGGRO); + m_creature->AddThreat(who, 5000000.0f); + } + + void UpdateAI(const uint32 diff) + { + advisorbase_ai::UpdateAI(diff); + + //Faking death, don't do anything + if (FakeDeath) + return; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Gaze_Timer + if(Gaze_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + DoResetThreat(); + m_creature->AddThreat(target, 5000000.0f); + DoTextEmote(EMOTE_THALADRED_GAZE, target); + Gaze_Timer = 8500; + } + }else Gaze_Timer -= diff; + + //Silence_Timer + if(Silence_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SILENCE); + Silence_Timer = 20000; + }else Silence_Timer -= diff; + + //PsychicBlow_Timer + if(PsychicBlow_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_PSYCHIC_BLOW); + PsychicBlow_Timer = 20000+rand()%5000; + }else PsychicBlow_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Lord Sanguinar AI +struct MANGOS_DLL_DECL boss_lord_sanguinarAI : public advisorbase_ai +{ + boss_lord_sanguinarAI(Creature *c) : advisorbase_ai(c){} + + uint32 Fear_Timer; + + void Reset() + { + Fear_Timer = 20000; + advisorbase_ai::Reset(); + } + + void JustDied(Unit* Killer) + { + DoPlaySoundToSet(m_creature, SOUND_SANGUINAR_DEATH); + DoYell(SAY_SANGUINAR_DEATH, LANG_UNIVERSAL, NULL); + } + + void Aggro(Unit *who) + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + if (!who || FakeDeath) + return; + + DoYell(SAY_SANGUINAR_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SANGUINAR_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + advisorbase_ai::UpdateAI(diff); + + //Faking death, don't do anything + if (FakeDeath) + return; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Fear_Timer + if(Fear_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_BELLOWING_ROAR); + Fear_Timer = 25000+rand()%10000; //approximately every 30 seconds + }else Fear_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Grand Astromancer Capernian AI +struct MANGOS_DLL_DECL boss_grand_astromancer_capernianAI : public advisorbase_ai +{ + boss_grand_astromancer_capernianAI(Creature *c) : advisorbase_ai(c){} + + uint32 Fireball_Timer; + uint32 Conflagration_Timer; + uint32 ArcaneExplosion_Timer; + uint32 Yell_Timer; + bool Yell; + + void Reset() + { + Fireball_Timer = 2000; + Conflagration_Timer = 20000; + ArcaneExplosion_Timer = 5000; + Yell_Timer = 2000; + Yell = false; + + advisorbase_ai::Reset(); + } + + void JustDied(Unit* pKiller) + { + DoPlaySoundToSet(m_creature, SOUND_CAPERNIAN_DEATH); + DoYell(SAY_CAPERNIAN_DEATH, LANG_UNIVERSAL, NULL); + } + + void AttackStart(Unit* who) + { + if (!who || FakeDeath || m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + if (who->isTargetableForAttack()) + { + //Begin attack + DoStartAttackAndMovement(who, CAPERNIAN_DISTANCE, M_PI/2); + + if (!InCombat) + { + Aggro(who); + InCombat = true; + } + } + } + + void Aggro(Unit *who) + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + if (!who || FakeDeath) + return; + } + + void UpdateAI(const uint32 diff) + { + advisorbase_ai::UpdateAI(diff); + + //Faking Death, don't do anything + if (FakeDeath) + return; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Yell_Timer + if(!Yell) + if(Yell_Timer < diff) + { + DoYell(SAY_CAPERNIAN_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_CAPERNIAN_AGGRO); + + Yell = true; + }else Yell_Timer -= diff; + + //Fireball_Timer + if(Fireball_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CAPERNIAN_FIREBALL); + Fireball_Timer = 4000; + }else Fireball_Timer -= diff; + + //Conflagration_Timer + if(Conflagration_Timer < diff) + { + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + if(target && m_creature->IsWithinDistInMap(target, 30)) + DoCast(target, SPELL_CONFLAGRATION); + else + DoCast(m_creature->getVictim(), SPELL_CONFLAGRATION); + + Conflagration_Timer = 10000+rand()%5000; + }else Conflagration_Timer -= diff; + + //ArcaneExplosion_Timer + if(ArcaneExplosion_Timer < diff) + { + bool InMeleeRange = false; + Unit *target = NULL; + std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); + for (std::list::iterator i = m_threatlist.begin(); i!= m_threatlist.end();++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + //if in melee range + if(pUnit && pUnit->IsWithinDistInMap(m_creature, 5)) + { + InMeleeRange = true; + target = pUnit; + break; + } + } + + if(InMeleeRange) + DoCast(target, SPELL_ARCANE_EXPLOSION); + + ArcaneExplosion_Timer = 4000+rand()%2000; + }else ArcaneExplosion_Timer -= diff; + + //Do NOT deal any melee damage. + } +}; + +//Master Engineer Telonicus AI +struct MANGOS_DLL_DECL boss_master_engineer_telonicusAI : public advisorbase_ai +{ + boss_master_engineer_telonicusAI(Creature *c) : advisorbase_ai(c){} + + uint32 Bomb_Timer; + uint32 RemoteToy_Timer; + + void Reset() + { + Bomb_Timer = 10000; + RemoteToy_Timer = 5000; + + advisorbase_ai::Reset(); + } + + void JustDied(Unit* pKiller) + { + DoPlaySoundToSet(m_creature, SOUND_TELONICUS_DEATH); + DoYell(SAY_TELONICUS_DEATH, LANG_UNIVERSAL, NULL); + } + + void Aggro(Unit *who) + { + if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) + return; + + if (!who || FakeDeath) + return; + + DoYell(SAY_TELONICUS_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_TELONICUS_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + advisorbase_ai::UpdateAI(diff); + + //Faking Death, do nothing + if (FakeDeath) + return; + + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Bomb_Timer + if(Bomb_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_BOMB); + Bomb_Timer = 25000; + }else Bomb_Timer -= diff; + + //RemoteToy_Timer + if(RemoteToy_Timer < diff) + { + Unit *target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + if(target) + DoCast(target, SPELL_REMOTE_TOY); + + RemoteToy_Timer = 10000+rand()%5000; + }else RemoteToy_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Flame Strike AI +struct MANGOS_DLL_DECL mob_kael_flamestrikeAI : public ScriptedAI +{ + mob_kael_flamestrikeAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Timer; + bool Casting; + bool KillSelf; + + void Reset() + { + Timer = 5000; + Casting = false; + KillSelf = false; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->setFaction(14); + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!Casting) + { + DoCast(m_creature, SPELL_FLAME_STRIKE_VIS); + Casting = true; + } + + //Timer + if(Timer < diff) + { + if (!KillSelf) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature, SPELL_FLAME_STRIKE_DMG); + }else m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + + KillSelf = true; + Timer = 1000; + }else Timer -= diff; + } +}; + +//Phoenix AI +struct MANGOS_DLL_DECL mob_phoenixAI : public ScriptedAI +{ + mob_phoenixAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Burn_Timer; + uint32 Hatch_Timer; + uint32 EggVis_Timer; + bool IsEgg; + + void Reset() + { + Burn_Timer = 1000; + Hatch_Timer = 15000; + EggVis_Timer = 0; + IsEgg = false; + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, PHOENIX_MODEL); + } + + void Hatch() + { + IsEgg = false; + m_creature->SetHealth(m_creature->GetMaxHealth()); + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, PHOENIX_MODEL); + Burn_Timer = 1000; + Hatch_Timer = 15000; + EggVis_Timer = 0; + AttackStart(m_creature->getVictim()); + } + + void DamageTaken(Unit* pKiller, uint32 &damage) + { + if (damage < m_creature->GetHealth()) + return; + + //Bird cannot die, only egg + if (!IsEgg) + { + //prevent death + damage = 0; + IsEgg = true; + + m_creature->GetMotionMaster()->Clear(); + m_creature->GetMotionMaster()->MoveIdle(); + m_creature->SetHealth(m_creature->GetMaxHealth()); + EggVis_Timer = 1000; + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,PLAYER_STATE_DEAD); + } + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + AttackStart(who); + } + } + } + + void UpdateAI(const uint32 diff) + { + //Check if we have a current target + if (!IsEgg) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (Burn_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_BURN); + Burn_Timer = 1000; + }else Burn_Timer -= diff; + + DoMeleeAttackIfReady(); + } + else + { + if (EggVis_Timer) + if (EggVis_Timer <= diff) + { + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID, PHOENIX_EGG_MODEL); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + EggVis_Timer = 0; + }else EggVis_Timer -= diff; + + if (Hatch_Timer < diff) + { + Hatch(); + }else Hatch_Timer -= diff; + } + } +}; + +CreatureAI* GetAI_boss_kaelthas(Creature *_Creature) +{ + return new boss_kaelthasAI (_Creature); +} + +CreatureAI* GetAI_boss_thaladred_the_darkener(Creature *_Creature) +{ + return new boss_thaladred_the_darkenerAI (_Creature); +} + +CreatureAI* GetAI_boss_lord_sanguinar(Creature *_Creature) +{ + return new boss_lord_sanguinarAI (_Creature); +} + +CreatureAI* GetAI_boss_grand_astromancer_capernian(Creature *_Creature) +{ + return new boss_grand_astromancer_capernianAI (_Creature); +} + +CreatureAI* GetAI_boss_master_engineer_telonicus(Creature *_Creature) +{ + return new boss_master_engineer_telonicusAI (_Creature); +} + +CreatureAI* GetAI_mob_kael_flamestrike(Creature *_Creature) +{ + return new mob_kael_flamestrikeAI (_Creature); +} + +CreatureAI* GetAI_mob_phoenix(Creature *_Creature) +{ + return new mob_phoenixAI (_Creature); +} + +void AddSC_boss_kaelthas() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_kaelthas"; + newscript->GetAI = GetAI_boss_kaelthas; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_thaladred_the_darkener"; + newscript->GetAI = GetAI_boss_thaladred_the_darkener; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_lord_sanguinar"; + newscript->GetAI = GetAI_boss_lord_sanguinar; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_grand_astromancer_capernian"; + newscript->GetAI = GetAI_boss_grand_astromancer_capernian; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_master_engineer_telonicus"; + newscript->GetAI = GetAI_boss_master_engineer_telonicus; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name= "mob_kael_flamestrike"; + newscript->GetAI = GetAI_mob_kael_flamestrike; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_phoenix"; + newscript->GetAI = GetAI_mob_phoenix; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_void_reaver.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_void_reaver.cpp new file mode 100644 index 00000000000..43072b1947d --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_void_reaver.cpp @@ -0,0 +1,203 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Void_Reaver +SD%Complete: 100 +SDComment: +SDCategory: Tempest Keep, The Eye +EndScriptData */ + +#include "precompiled.h" +#include "def_the_eye.h" + +#define SPELL_POUNDING 34162 +#define SPELL_ARCANE_ORB_TRIGGER 34172 +#define SPELL_KNOCK_AWAY 11130 +#define SPELL_BERSERK 27680 + +#define SAY_AGGRO "Alert, you are marked for extermination!" +#define SAY_SLAY1 "Extermination, successful." +#define SAY_SLAY2 "Imbecile life form, no longer functional." +#define SAY_SLAY3 "Threat neutralized." +#define SAY_DEATH "Systems... shutting... down..." +#define SAY_POUNDING1 "Alternative measure commencing..." +#define SAY_POUNDING2 "Calculating force parameters..." + +#define SOUND_AGGRO 11213 +#define SOUND_SLAY1 11215 +#define SOUND_SLAY2 11216 +#define SOUND_SLAY3 11217 +#define SOUND_DEATH 11214 +#define SOUND_POUNDING1 11218 +#define SOUND_POUNDING2 11219 + +#define CREATURE_ORB_TARGET 19577 + +struct MANGOS_DLL_DECL boss_void_reaverAI : public ScriptedAI +{ + boss_void_reaverAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance* pInstance; + + uint32 Pounding_Timer; + uint32 ArcaneOrb_Timer; + uint32 KnockAway_Timer; + uint32 Berserk_Timer; + + void Reset() + { + Pounding_Timer = 12000; + ArcaneOrb_Timer = 3000; + KnockAway_Timer = 30000; + Berserk_Timer = 600000; + + if(pInstance) + pInstance->SetData(DATA_VOIDREAVEREVENT, NOT_STARTED); + } + + void KilledUnit(Unit *victim) + { + switch(rand()%3) + { + case 0: + DoYell(SAY_SLAY1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY1); + break; + case 1: + DoYell(SAY_SLAY2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY2); + break; + case 2: + DoYell(SAY_SLAY3, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_SLAY3); + break; + } + } + + void JustDied(Unit *victim) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + + if(pInstance) + pInstance->SetData(DATA_VOIDREAVEREVENT, NOT_STARTED); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_AGGRO); + + if(pInstance) + pInstance->SetData(DATA_VOIDREAVEREVENT, IN_PROGRESS); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + // Pounding + if(Pounding_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_POUNDING); + + switch(rand()%2) + { + case 0: + DoPlaySoundToSet(m_creature, SOUND_POUNDING1); + DoYell(SAY_POUNDING1, LANG_UNIVERSAL, NULL); + break; + case 1: + DoPlaySoundToSet(m_creature, SOUND_POUNDING2); + DoYell(SAY_POUNDING2, LANG_UNIVERSAL, NULL); + break; + } + Pounding_Timer = 12000; + }else Pounding_Timer -= diff; + + // Arcane Orb + if(ArcaneOrb_Timer < diff) + { + Unit *target; + std::list t_list = m_creature->getThreatManager().getThreatList(); + std::vector target_list; + for(std::list::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr) + { + target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()); + //15 yard radius minimum + if(target && target->GetDistance2d(m_creature) > 15) + target_list.push_back(target); + target = NULL; + } + if(target_list.size()) + target = *(target_list.begin()+rand()%target_list.size()); + + if (target) + { + Unit* Spawn = NULL; + Spawn = m_creature->SummonCreature(CREATURE_ORB_TARGET, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 10000); + if (Spawn) + m_creature->CastSpell(Spawn, SPELL_ARCANE_ORB_TRIGGER, true); + } + + ArcaneOrb_Timer = 3000; + }else ArcaneOrb_Timer -= diff; + + // Single Target knock back, reduces aggro + if(KnockAway_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCK_AWAY); + + //Drop 25% aggro + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-25); + + KnockAway_Timer = 30000; + }else KnockAway_Timer -= diff; + + //Berserk + if(Berserk_Timer < diff) + { + if (m_creature->IsNonMeleeSpellCasted(false)) + m_creature->InterruptNonMeleeSpells(false); + + DoCast(m_creature,SPELL_BERSERK); + Berserk_Timer = 600000; + }else Berserk_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_void_reaver(Creature *_Creature) +{ + return new boss_void_reaverAI (_Creature); +} + +void AddSC_boss_void_reaver() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_void_reaver"; + newscript->GetAI = GetAI_boss_void_reaver; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/def_the_eye.h b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/def_the_eye.h new file mode 100644 index 00000000000..eb7a9ecedf1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/def_the_eye.h @@ -0,0 +1,20 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_THE_EYE_H +#define DEF_THE_EYE_H + +#define DATA_ALAREVENT 1 +#define DATA_ASTROMANCER 2 +#define DATA_GRANDASTROMANCERCAPERNIAN 3 +#define DATA_HIGHASTROMANCERSOLARIANEVENT 4 +#define DATA_FATHOMLORDKARATHRESSEVENT 5 +#define DATA_KAELTHAS 6 +#define DATA_KAELTHASEVENT 7 +#define DATA_LORDSANGUINAR 8 +#define DATA_MASTERENGINEERTELONICUS 9 +#define DATA_SOLARIANEVENT 10 +#define DATA_THALADREDTHEDARKENER 11 +#define DATA_VOIDREAVEREVENT 12 +#endif diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/instance_the_eye.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/instance_the_eye.cpp new file mode 100644 index 00000000000..b2bd2920a82 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/instance_the_eye.cpp @@ -0,0 +1,178 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_The_Eye +SD%Complete: 100 +SDComment: +SDCategory: Tempest Keep, The Eye +EndScriptData */ + +#include "precompiled.h" +#include "def_the_eye.h" + +#define ENCOUNTERS 5 + +/* The Eye encounters: +0 - Kael'thas event +1 - Al' ar event +2 - Solarian Event +3 - Void Reaver event +*/ + +struct MANGOS_DLL_DECL instance_the_eye : public ScriptedInstance +{ + instance_the_eye(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint64 ThaladredTheDarkener; + uint64 LordSanguinar; + uint64 GrandAstromancerCapernian; + uint64 MasterEngineerTelonicus; + uint64 Kaelthas; + uint64 Astromancer; + + uint8 KaelthasEventPhase; + + bool Encounters[ENCOUNTERS]; + + void Initialize() + { + ThaladredTheDarkener = 0; + LordSanguinar = 0; + GrandAstromancerCapernian = 0; + MasterEngineerTelonicus = 0; + Kaelthas = 0; + Astromancer = 0; + + KaelthasEventPhase = 0; + + for(uint8 i = 0; i < ENCOUNTERS; i++) + Encounters[i] = false; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < ENCOUNTERS; i++) + if(Encounters[i]) return true; + + return false; + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case 20064: ThaladredTheDarkener = creature->GetGUID(); break; + case 20063: MasterEngineerTelonicus = creature->GetGUID(); break; + case 20062: GrandAstromancerCapernian = creature->GetGUID(); break; + case 20060: LordSanguinar = creature->GetGUID(); break; + case 19622: Kaelthas = creature->GetGUID(); break; + case 18805: Astromancer = creature->GetGUID(); break; + } + } + + uint64 GetData64(uint32 identifier) + { + switch(identifier) + { + case DATA_THALADREDTHEDARKENER: + return ThaladredTheDarkener; + + case DATA_LORDSANGUINAR: + return LordSanguinar; + + case DATA_GRANDASTROMANCERCAPERNIAN: + return GrandAstromancerCapernian; + + case DATA_MASTERENGINEERTELONICUS: + return MasterEngineerTelonicus; + + case DATA_KAELTHAS: + return Kaelthas; + + case DATA_ASTROMANCER: + return Astromancer; + } + + return 0; + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_ALAREVENT: + Encounters[0] = (data) ? true : false; + break; + + case DATA_SOLARIANEVENT: + Encounters[1] = (data) ? true : false; + break; + + case DATA_VOIDREAVEREVENT: + Encounters[2] = (data) ? true : false; + break; + + //Kael'thas + case DATA_KAELTHASEVENT: + KaelthasEventPhase = data; + Encounters[3] = (data) ? true : false; + break; + + case DATA_HIGHASTROMANCERSOLARIANEVENT: + Encounters[4] = (data) ? true : false; + break; + } + } + + uint32 GetData(uint32 type) + { + switch(type) + { + case DATA_ALAREVENT: + return Encounters[0]; + + case DATA_SOLARIANEVENT: + return Encounters[1]; + + case DATA_VOIDREAVEREVENT: + return Encounters[2]; + + case DATA_HIGHASTROMANCERSOLARIANEVENT: + return Encounters[4]; + + //Kael'thas + case DATA_KAELTHASEVENT: + return KaelthasEventPhase; + } + + return 0; + } +}; + +InstanceData* GetInstanceData_instance_the_eye(Map* map) +{ + return new instance_the_eye(map); +} + +void AddSC_instance_the_eye() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_the_eye"; + newscript->GetInstanceData = GetInstanceData_instance_the_eye; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/the_eye.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/the_eye.cpp new file mode 100644 index 00000000000..055a7ad3095 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/the_eye.cpp @@ -0,0 +1,98 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: The_Eye +SD%Complete: 100 +SDComment: +SDCategory: Tempest Keep, The Eye +EndScriptData */ + +/* ContentData +mob_crystalcore_devastator +EndContentData */ + +#include "precompiled.h" +#include "def_the_eye.h" + +#define SPELL_COUNTERCHARGE 35035 +#define SPELL_KNOCKAWAY 22893 + +struct MANGOS_DLL_DECL mob_crystalcore_devastatorAI : public ScriptedAI +{ + mob_crystalcore_devastatorAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Knockaway_Timer; + uint32 Countercharge_Timer; + + void Reset() + { + Countercharge_Timer = 9000; + Knockaway_Timer = 25000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Check if we have a current target + //Knockaway_Timer + if (Knockaway_Timer < diff) + { + m_creature->CastSpell(m_creature->getVictim(),SPELL_KNOCKAWAY, true); + + // current aggro target is knocked away pick new target + Unit* Target = SelectUnit(SELECT_TARGET_TOPAGGRO, 0); + + if (!Target || Target == m_creature->getVictim()) + Target = SelectUnit(SELECT_TARGET_TOPAGGRO, 1); + + if (Target) + m_creature->TauntApply(Target); + + Knockaway_Timer = 23000; + } + else Knockaway_Timer -= diff; + + //Countercharge_Timer + if (Countercharge_Timer < diff) + { + DoCast(this->m_creature,SPELL_COUNTERCHARGE); + Countercharge_Timer = 45000; + }else Countercharge_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_crystalcore_devastator(Creature *_Creature) +{ + return new mob_crystalcore_devastatorAI (_Creature); +} + +void AddSC_the_eye() +{ + Script *newscript; + newscript = new Script; + newscript->Name="mob_crystalcore_devastator"; + newscript->GetAI = GetAI_mob_crystalcore_devastator; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp new file mode 100644 index 00000000000..f7aab61b6bb --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp @@ -0,0 +1,24 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss Gatewatcher Gyrokill +SD%Complete: 0 +SDComment: Place Holder +SDCategory: Tempest Keep, The Mechanar +EndScriptData */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp new file mode 100644 index 00000000000..77d57ff9b4c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp @@ -0,0 +1,147 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss Gatewatcher Ironhand +SD%Complete: 100 +SDComment: +SDCategory: Tempest Keep, The Mechanar +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SHADOW_POWER 35322 +#define SPELL_JACKHAMMER 35326 +#define SPELL_STREAM_OF_MACHINE_FLUID 35311 + +#define SAY_SPELL_JACKHAMMER_1 "With the precise angle and velocity... " +#define SOUND_SPELL_JACKHAMMER_1 11112 +#define SAY_SPELL_JACKHAMMER_2 "Low tech yet quiet effective!" +#define SOUND_SPELL_JACKHAMMER_2 11113 + +#define SAY_AGGRO_1 "You have approximately five seconds to live." +#define SOUND_SAY_AGGRO_1 11109 + +#define SAY_SLAY_1 "A foregone conclusion." +#define SOUND_SLAY_1 11110 +#define SAY_SLAY_2 "The processing will continue a schedule!" +#define SOUND_SLAY_2 11111 + +#define SAY_DEATH_1 "My calculations did not... " +#define SOUND_DEATH_1 11114 + + +struct MANGOS_DLL_DECL boss_gatewatcher_iron_handAI : public ScriptedAI +{ + boss_gatewatcher_iron_handAI(Creature *c) : ScriptedAI(c) { Reset(); } + + uint32 Shadow_Power_Timer; + uint32 Jackhammer_Timer; + uint32 Stream_of_Machine_Fluid_Timer; + + void Reset() + { + Shadow_Power_Timer = 25000; + Jackhammer_Timer = 45000; + Stream_of_Machine_Fluid_Timer = 55000; + } + + void Aggro(Unit *who) + { + } + + void KilledUnit(Unit* victim) + { + if (rand()%2) + return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_1); + break; + case 1: + DoYell(SAY_SLAY_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_2); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH_1,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH_1); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Shadow Power + if(Shadow_Power_Timer < diff) + { + DoCast(m_creature,SPELL_SHADOW_POWER); + Shadow_Power_Timer = 25000; + }else Shadow_Power_Timer -= diff; + + //Jack Hammer + if(Jackhammer_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_JACKHAMMER); + + if (rand()%2) + return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_SPELL_JACKHAMMER_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SPELL_JACKHAMMER_1); + break; + case 1: + DoYell(SAY_SPELL_JACKHAMMER_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SPELL_JACKHAMMER_2); + break; + } + + Jackhammer_Timer = 45000; + }else Jackhammer_Timer -= diff; + + //Stream of Machine Fluid + if(Stream_of_Machine_Fluid_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_STREAM_OF_MACHINE_FLUID); + Stream_of_Machine_Fluid_Timer = 55000; + }else Stream_of_Machine_Fluid_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_gatewatcher_iron_hand(Creature *_Creature) +{ + return new boss_gatewatcher_iron_handAI (_Creature); +} + +void AddSC_boss_gatewatcher_iron_hand() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_gatewatcher_iron_hand"; + newscript->GetAI = GetAI_boss_gatewatcher_iron_hand; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp new file mode 100644 index 00000000000..e5012885d93 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp @@ -0,0 +1,223 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss Nethermancer Sepethrea +SD%Complete: 100 +SDComment: +SDCategory: Tempest Keep, The Mechanar +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_SUMMON_RAGIN_FLAMES 35275 +#define SPELL_INFERNO 19695 +#define SPELL_FIRE_TAIL 35278 + +#define SPELL_FROST_ATTACK 35263 +#define SPELL_ARCANE_BLAST 35314 +#define SPELL_DRAGONS_BREATH 35250 +#define SPELL_KNOCKBACK 37317 +#define SPELL_SOLARBURN 35267 + +#define SAY_SPELL_DRAGONS_BREATH_1 "Think you can take the heat?" +#define SOUND_SPELL_DRAGONS_BREATH_1 11189 +#define SAY_SPELL_DRAGONS_BREATH_2 "Anar'endal dracon!" +#define SOUND_SPELL_DRAGONS_BREATH_2 11190 + +#define SAY_AGGRO "Don't value your life very much, do you?" +#define SOUND_SAY_AGGRO 11186 + +#define SAY_SLAY "And don't come back!" +#define SOUND_SLAY 11187 + +#define SAY_DEATH "Anu... bala belore...alon." +#define SOUND_DEATH 11192 + +struct MANGOS_DLL_DECL boss_nethermancer_sepethreaAI : public ScriptedAI +{ + boss_nethermancer_sepethreaAI(Creature *c) : ScriptedAI(c) { Reset(); } + + uint32 frost_attack_Timer; + uint32 arcane_blast_Timer; + uint32 dragons_breath_Timer; + uint32 knockback_Timer; + uint32 solarburn_Timer; + + void Reset() + { + frost_attack_Timer = 10000; + arcane_blast_Timer = 15000; + dragons_breath_Timer = 20000; + knockback_Timer = 25000; + solarburn_Timer = 30000; + } + + void Aggro(Unit *who) + { + } + + void KilledUnit(Unit* victim) + { + DoYell(SAY_SLAY, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_DEATH); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Frost Attack + if(frost_attack_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROST_ATTACK); + frost_attack_Timer = 10000; + }else frost_attack_Timer -= diff; + + //Arcane Blast + if(arcane_blast_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ARCANE_BLAST); + arcane_blast_Timer = 15000; + }else arcane_blast_Timer -= diff; + + //Dragons Breath + if(dragons_breath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_DRAGONS_BREATH); + dragons_breath_Timer = 20000; + + if (rand()%2) + return; + + switch(rand()%2) + { + case 0: + DoYell(SAY_SPELL_DRAGONS_BREATH_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SPELL_DRAGONS_BREATH_1); + break; + case 1: + DoYell(SAY_SPELL_DRAGONS_BREATH_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SPELL_DRAGONS_BREATH_2); + break; + } + }else dragons_breath_Timer -= diff; + + //Knockback + if(knockback_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCKBACK); + knockback_Timer = 25000; + }else knockback_Timer -= diff; + + //Solarburn + if(solarburn_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SOLARBURN); + solarburn_Timer = 30000; + }else solarburn_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_nethermancer_sepethrea(Creature *_Creature) +{ + return new boss_nethermancer_sepethreaAI (_Creature); +} + +struct MANGOS_DLL_DECL mob_ragin_flamesAI : public ScriptedAI +{ + mob_ragin_flamesAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 inferno_Timer; + uint32 flame_timer; + + bool onlyonce; + + void Reset() + { + inferno_Timer = 10000; + flame_timer = 200; + onlyonce = false; + } + + void Aggro(Unit* who) + { + } + + void UpdateAI(const uint32 diff) + { + Unit* target = NULL; + + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (!onlyonce) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + m_creature->GetMotionMaster()->MoveChase(target); + onlyonce = true; + } + + if(inferno_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_INFERNO); + inferno_Timer = 10000; + + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + m_creature->GetMotionMaster()->MoveChase(target); + }else inferno_Timer -= diff; + + if(flame_timer < diff) + { + DoCast(m_creature,SPELL_FIRE_TAIL); + flame_timer = 200; + }else flame_timer -=diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_mob_ragin_flames(Creature *_Creature) +{ + return new mob_ragin_flamesAI (_Creature); +} + +void AddSC_boss_nethermancer_sepethrea() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_nethermancer_sepethrea"; + newscript->GetAI = GetAI_boss_nethermancer_sepethrea; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_ragin_flames"; + newscript->GetAI = GetAI_mob_ragin_flames; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_bug_trio.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_bug_trio.cpp new file mode 100644 index 00000000000..2592d8e8297 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_bug_trio.cpp @@ -0,0 +1,384 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: boss_kri, boss_yauj, boss_vem : The Bug Trio +SD%Complete: 100 +SDComment: +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "def_temple_of_ahnqiraj.h" + +#define SPELL_CLEAVE 26350 +#define SPELL_TOXIC_VOLLEY 25812 +#define SPELL_POISON_CLOUD 38718 //Only Spell with right dmg. +#define SPELL_ENRAGE 34624 //Changed cause 25790 is casted on gamers too. Same prob with old explosion of twin emperors. + +#define SPELL_CHARGE 26561 +#define SPELL_KNOCKBACK 26027 + +#define SPELL_HEAL 25807 +#define SPELL_FEAR 19408 + +struct MANGOS_DLL_DECL boss_kriAI : public ScriptedAI +{ + boss_kriAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 Cleave_Timer; + uint32 ToxicVolley_Timer; + uint32 Check_Timer; + + bool VemDead; + bool Death; + + void Reset() + { + Cleave_Timer = 4000 + rand()%4000; + ToxicVolley_Timer = 6000 + rand()%6000; + Check_Timer = 2000; + + VemDead = false; + Death = false; + } + + void Aggro(Unit *who) + { + } + + void JustDied(Unit* killer) + { + if(pInstance) + { + if(pInstance->GetData(DATA_BUG_TRIO_DEATH) < 2) + // Unlootable if death + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + + pInstance->SetData(DATA_BUG_TRIO_DEATH, 1); + } + } + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Cleave_Timer + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 5000 + rand()%7000; + }else Cleave_Timer -= diff; + + //ToxicVolley_Timer + if (ToxicVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_TOXIC_VOLLEY); + ToxicVolley_Timer = 10000 + rand()%5000; + }else ToxicVolley_Timer -= diff; + + if (m_creature->GetHealth() <= m_creature->GetMaxHealth() * 0.05 && !Death) + { + DoCast(m_creature->getVictim(),SPELL_POISON_CLOUD); + Death = true; + } + + if(!VemDead) + { + //Checking if Vem is dead. If yes we will enrage. + if(Check_Timer < diff) + { + if(pInstance && pInstance->GetData(DATA_VEMISDEAD)) + { + DoCast(m_creature, SPELL_ENRAGE); + VemDead = true; + } + Check_Timer = 2000; + }else Check_Timer -=diff; + } + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_vemAI : public ScriptedAI +{ + boss_vemAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 Charge_Timer; + uint32 KnockBack_Timer; + uint32 Enrage_Timer; + + bool Enraged; + + void Reset() + { + Charge_Timer = 15000 + rand()%12000; + KnockBack_Timer = 8000 + rand()%12000; + Enrage_Timer = 120000; + + Enraged = false; + } + + void JustDied(Unit* Killer) + { + if(pInstance) + { + pInstance->SetData(DATA_VEM_DEATH, 0); + if(pInstance->GetData(DATA_BUG_TRIO_DEATH) < 2) + // Unlootable if death + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + pInstance->SetData(DATA_BUG_TRIO_DEATH, 1); + } + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && IsVisible(who) && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + //Begin melee attack if we are within range + DoStartAttackAndMovement(who); + } + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Charge_Timer + if (Charge_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if(target) + { + DoCast(target, SPELL_CHARGE); + m_creature->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true,1); + DoStartAttackAndMovement(target); + } + + Charge_Timer = 8000 + rand()%8000; + }else Charge_Timer -= diff; + + //KnockBack_Timer + if (KnockBack_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KNOCKBACK); + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-80); + KnockBack_Timer = 15000 + rand()%10000; + }else KnockBack_Timer -= diff; + + //Enrage_Timer + if (!Enraged && Enrage_Timer < diff) + { + DoCast(m_creature,SPELL_ENRAGE); + Enraged = true; + }else Charge_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_yaujAI : public ScriptedAI +{ + boss_yaujAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 Heal_Timer; + uint32 Fear_Timer; + uint32 Check_Timer; + + bool VemDead; + + void Reset() + { + Heal_Timer = 25000 + rand()%15000; + Fear_Timer = 12000 + rand()%12000; + Check_Timer = 2000; + + VemDead = false; + } + + void JustDied(Unit* Killer) + { + if(pInstance) + { + if(pInstance->GetData(DATA_BUG_TRIO_DEATH) < 2) + // Unlootable if death + m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + pInstance->SetData(DATA_BUG_TRIO_DEATH, 1); + } + + for(int i = 0; i < 10;i++) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0); + Creature* Summoned = m_creature->SummonCreature(15621,m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(),0,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,90000); + if(Summoned) + ((CreatureAI*)Summoned->AI())->AttackStart(target); + } + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && IsVisible(who) && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + //Begin melee attack if we are within range + DoStartAttackAndMovement(who); + } + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Fear_Timer + if (Fear_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FEAR); + DoResetThreat(); + Fear_Timer = 20000; + }else Fear_Timer -= diff; + + //Casting Heal to other twins or herself. + if(Heal_Timer < diff) + { + if(pInstance) + { + Unit *pKri = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KRI)); + Unit *pVem = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_VEM)); + + switch(rand()%3) + { + case 0: + if(pKri) + DoCast(pKri, SPELL_HEAL); + break; + case 1: + if(pVem) + DoCast(pVem, SPELL_HEAL); + break; + case 2: + DoCast(m_creature, SPELL_HEAL); + break; + } + } + + Heal_Timer = 15000+rand()%15000; + }else Heal_Timer -= diff; + + //Checking if Vem is dead. If yes we will enrage. + if(Check_Timer < diff) + { + if (!VemDead) + { + if(pInstance) + { + if(pInstance->GetData(DATA_VEMISDEAD)) + { + DoCast(m_creature, SPELL_ENRAGE); + VemDead = true; + } + } + } + Check_Timer = 2000; + }else Check_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_yauj(Creature *_Creature) +{ + return new boss_yaujAI (_Creature); +} + +CreatureAI* GetAI_boss_vem(Creature *_Creature) +{ + return new boss_vemAI (_Creature); +} + +CreatureAI* GetAI_boss_kri(Creature *_Creature) +{ + return new boss_kriAI (_Creature); +} + +void AddSC_bug_trio() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_kri"; + newscript->GetAI = GetAI_boss_kri; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_vem"; + newscript->GetAI = GetAI_boss_vem; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_yauj"; + newscript->GetAI = GetAI_boss_yauj; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_cthun.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_cthun.cpp new file mode 100644 index 00000000000..a01de84604b --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_cthun.cpp @@ -0,0 +1,1360 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Cthun +SD%Complete: 95 +SDComment: Darkglare tracking issue +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "def_temple_of_ahnqiraj.h" + +#define PI 3.14 + +//****** Out of Combat ****** +//Random Wispers - No txt only sound +#define RND_WISPER_1 8580 //Death is close +#define RND_WISPER_2 8581 //You are already dead +#define RND_WISPER_3 8582 //Your courage will fail +#define RND_WISPER_4 8583 //Your friends will abandon you +#define RND_WISPER_5 8584 //You will betray your friends +#define RND_WISPER_6 8585 //You will die +#define RND_WISPER_7 8586 //You are weak +#define RND_WISPER_8 8587 //Your heart will explode + +//***** Phase 1 ******** + +//Mobs +#define BOSS_EYE_OF_CTHUN 15589 +#define MOB_CLAW_TENTACLE 15725 +#define MOB_EYE_TENTACLE 15726 +#define MOB_SMALL_PORTAL 15904 + +//Eye Spells +#define SPELL_GREEN_BEAM 26134 +#define SPELL_DARK_GLARE 26029 +#define SPELL_RED_COLORATION 22518 //Probably not the right spell but looks similar + +//Eye Tentacles Spells +#define SPELL_MIND_FLAY 26143 + +//Claw Tentacles Spells +#define SPELL_GROUND_RUPTURE 26139 +#define SPELL_HAMSTRING 26141 + +#define MOB_ + +//*****Phase 2****** +//Body spells +//#define SPELL_CARAPACE_CTHUN 26156 //Was removed from client dbcs +#define SPELL_TRANSFORM 26232 + +//Eye Tentacles Spells +//SAME AS PHASE1 + +//Giant Claw Tentacles +#define SPELL_MASSIVE_GROUND_RUPTURE 26100 + +//Also casts Hamstring +#define SPELL_THRASH 3391 + +//Giant Eye Tentacles +//CHAIN CASTS "SPELL_GREEN_BEAM" + +//Stomach Spells +#define SPELL_MOUTH_TENTACLE 26332 +#define SPELL_EXIT_STOMACH_KNOCKBACK 25383 +#define SPELL_DIGESTIVE_ACID 26476 + +//Mobs +#define MOB_BODY_OF_CTHUN 15809 +#define MOB_GIANT_CLAW_TENTACLE 15728 +#define MOB_GIANT_EYE_TENTACLE 15334 +#define MOB_FLESH_TENTACLE 15802 +#define MOB_GIANT_PORTAL 15910 + +//Text emote +#define EMOTE_WEAKENED "is weakened!" + +//Stomach Teleport positions +#define STOMACH_X -8562.0f +#define STOMACH_Y 2037.0f +#define STOMACH_Z -70.0f +#define STOMACH_O 5.05f + +//Flesh tentacle positions +#define TENTACLE_POS1_X -8571.0f +#define TENTACLE_POS1_Y 1990.0f +#define TENTACLE_POS1_Z -98.0f +#define TENTACLE_POS1_O 1.22f + +#define TENTACLE_POS2_X -8525.0f +#define TENTACLE_POS2_Y 1994.0f +#define TENTACLE_POS2_Z -98.0f +#define TENTACLE_POS2_O 2.12f + +//Kick out position +#define KICK_X -8545.0f +#define KICK_Y 1984.0f +#define KICK_Z -96.0f + +struct MANGOS_DLL_DECL flesh_tentacleAI : public Scripted_NoMovementAI +{ + flesh_tentacleAI(Creature *c) : Scripted_NoMovementAI(c), Parent(0) {Reset();} + + uint64 Parent; + uint32 CheckTimer; + + void SpawnedByCthun(uint64 p) + { + Parent = p; + } + + void Reset() + { + CheckTimer = 1000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff); + + void JustDied(Unit* killer); +}; + +struct MANGOS_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI +{ + eye_of_cthunAI(Creature *c) : Scripted_NoMovementAI(c) + { + pInst = (ScriptedInstance*)c->GetInstanceData(); + if (!pInst) + error_log("SD2: No Instance eye_of_cthunAI"); + + Reset(); + } + + ScriptedInstance* pInst; + + //Global variables + uint32 PhaseTimer; + + //Eye beam phase + uint32 BeamTimer; + uint32 EyeTentacleTimer; + uint32 ClawTentacleTimer; + + //Dark Glare phase + uint32 DarkGlareTick; + uint32 DarkGlareTickTimer; + float DarkGlareAngle; + bool ClockWise; + + void Reset() + { + //Phase information + PhaseTimer = 50000; //First dark glare in 50 seconds + + //Eye beam phase 50 seconds + BeamTimer = 3000; + EyeTentacleTimer = 45000; //Always spawns 5 seconds before Dark Beam + ClawTentacleTimer = 12500; //4 per Eye beam phase (unsure if they spawn durring Dark beam) + + //Dark Beam phase 35 seconds (each tick = 1 second, 35 ticks) + DarkGlareTick = 0; + DarkGlareTickTimer = 1000; + DarkGlareAngle = 0; + ClockWise = false; + + //Reset flags + m_creature->RemoveAurasDueToSpell(SPELL_RED_COLORATION); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + + //Reset Phase + if (pInst) + pInst->SetData(DATA_CTHUN_PHASE, 0); + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + } + + void SpawnEyeTentacle(float x, float y) + { + Creature* Spawned; + Spawned = (Creature*)m_creature->SummonCreature(MOB_EYE_TENTACLE,m_creature->GetPositionX()+x,m_creature->GetPositionY()+y,m_creature->GetPositionZ(),0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,500); + if (Spawned) + { + Unit* target; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + if (target) + Spawned->AI()->AttackStart(target); + } + } + + void UpdateAI(const uint32 diff) + { + //Check if we have a target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //No instance + if (!pInst) + return; + + switch (pInst->GetData(DATA_CTHUN_PHASE)) + { + case 0: + { + //BeamTimer + if (BeamTimer < diff) + { + //SPELL_GREEN_BEAM + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(target,SPELL_GREEN_BEAM); + + //Correctly update our target + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, target->GetGUID()); + } + + //Beam every 3 seconds + BeamTimer = 3000; + }else BeamTimer -= diff; + + //ClawTentacleTimer + if (ClawTentacleTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + { + Creature* Spawned = NULL; + + //Spawn claw tentacle on the random target + Spawned = (Creature*)m_creature->SummonCreature(MOB_CLAW_TENTACLE,target->GetPositionX(),target->GetPositionY(),target->GetPositionZ(),0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,500); + + if (Spawned) + Spawned->AI()->AttackStart(target); + } + + //One claw tentacle every 12.5 seconds + ClawTentacleTimer = 12500; + }else ClawTentacleTimer -= diff; + + //EyeTentacleTimer + if (EyeTentacleTimer < diff) + { + //Spawn the 8 Eye Tentacles in the corret spots + SpawnEyeTentacle(0, 20); //south + SpawnEyeTentacle(10, 10); //south west + SpawnEyeTentacle(20, 0); //west + SpawnEyeTentacle(10, -10); //north west + + SpawnEyeTentacle(0, -20); //north + SpawnEyeTentacle(-10, -10); //north east + SpawnEyeTentacle(-20, 0); // east + SpawnEyeTentacle(-10, 10); // south east + + //No point actually putting a timer here since + //These shouldn't trigger agian until after phase shifts + EyeTentacleTimer = 45000; + }else EyeTentacleTimer -= diff; + + //PhaseTimer + if (PhaseTimer < diff) + { + //Switch to Dark Beam + pInst->SetData(DATA_CTHUN_PHASE, 1); + + m_creature->InterruptNonMeleeSpells(false); + + //Select random target for dark beam to start on + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + if (target) + { + //Correctly update our target + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, target->GetGUID()); + + //Face our target + DarkGlareAngle = m_creature->GetAngle(target); + DarkGlareTickTimer = 1000; + DarkGlareTick = 0; + ClockWise = rand()%2; + } + + //Add red coloration to C'thun + DoCast(m_creature,SPELL_RED_COLORATION); + + //Freeze animation + m_creature->setEmoteState(53); + + //Darkbeam for 35 seconds + PhaseTimer = 35000; + }else PhaseTimer -= diff; + + } + break; + case 1: + { + //EyeTentacleTimer + if (DarkGlareTick < 35) + if (DarkGlareTickTimer < diff) + { + //Remove any target + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0); + + //Set angle and cast + if (ClockWise) + m_creature->SetOrientation(DarkGlareAngle + ((float)DarkGlareTick*PI/35)); + else m_creature->SetOrientation(DarkGlareAngle - ((float)DarkGlareTick*PI/35)); + + m_creature->StopMoving(); + + //Actual dark glare cast, maybe something missing here? + m_creature->CastSpell(NULL, SPELL_DARK_GLARE, false); + + //Increase tick + DarkGlareTick++; + + //1 second per tick + DarkGlareTickTimer = 1000; + }else DarkGlareTickTimer -= diff; + + //PhaseTimer + if (PhaseTimer < diff) + { + //Switch to Eye Beam + pInst->SetData(DATA_CTHUN_PHASE, 0); + + BeamTimer = 3000; + EyeTentacleTimer = 45000; //Always spawns 5 seconds before Dark Beam + ClawTentacleTimer = 12500; //4 per Eye beam phase (unsure if they spawn durring Dark beam) + + m_creature->InterruptNonMeleeSpells(false); + + //Remove Red coloration from c'thun + m_creature->RemoveAurasDueToSpell(SPELL_RED_COLORATION); + + //Freeze animation + m_creature->setEmoteState(0); + m_creature->SetUInt32Value(UNIT_FIELD_FLAGS, 0); + + //Eye Beam for 50 seconds + PhaseTimer = 50000; + }else PhaseTimer -= diff; + }break; + + //Transition phase + case 2: + { + //Remove any target + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0); + m_creature->SetHealth(0); + } + + //Dead phase + case 5: + { + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + } + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + //No instance + if (!pInst) + return; + + switch (pInst->GetData(DATA_CTHUN_PHASE)) + { + case 0: + case 1: + { + //Only if it will kill + if (damage < m_creature->GetHealth()) + return; + + //Fake death in phase 0 or 1 (green beam or dark glare phase) + m_creature->InterruptNonMeleeSpells(false); + + //Remove Red coloration from c'thun + m_creature->RemoveAurasDueToSpell(SPELL_RED_COLORATION); + + //Reset to normal emote state and prevent select and attack + m_creature->setEmoteState(0); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + + //Remove Target field + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0); + + //Death animation/respawning; + pInst->SetData(DATA_CTHUN_PHASE, 2); + + m_creature->SetHealth(0); + damage = 0; + + m_creature->InterruptNonMeleeSpells(true); + m_creature->RemoveAllAuras(); + } + break; + + case 5: + { + //Allow death here + return; + } + + default: + { + //Prevent death in this phase + damage = 0; + return; + } + break; + } + } +}; + +struct MANGOS_DLL_DECL cthunAI : public Scripted_NoMovementAI +{ + cthunAI(Creature *c) : Scripted_NoMovementAI(c) + { + pInst = (ScriptedInstance*)c->GetInstanceData(); + if (!pInst) + error_log("SD2: No Instance eye_of_cthunAI"); + + Reset(); + } + + ScriptedInstance* pInst; + + //Out of combat whisper timer + uint32 WisperTimer; + + //Global variables + uint32 PhaseTimer; + + //------------------- + + //Phase transition + uint64 HoldPlayer; + + //Body Phase + uint32 EyeTentacleTimer; + uint8 FleshTentaclesKilled; + uint32 GiantClawTentacleTimer; + uint32 GiantEyeTentacleTimer; + uint32 StomachAcidTimer; + uint32 StomachEnterTimer; + uint32 StomachEnterVisTimer; + uint64 StomachEnterTarget; + + //Stomach map, bool = true then in stomach + HM_NAMESPACE::hash_map Stomach_Map; + + void Reset() + { + //One random wisper every 90 - 300 seconds + WisperTimer = 90000; + + //Phase information + PhaseTimer = 10000; //Emerge in 10 seconds + + //No hold player for transition + HoldPlayer = 0; + + //Body Phase + EyeTentacleTimer = 30000; + FleshTentaclesKilled = 0; + GiantClawTentacleTimer = 15000; //15 seconds into body phase (1 min repeat) + GiantEyeTentacleTimer = 45000; //15 seconds into body phase (1 min repeat) + StomachAcidTimer = 4000; //Every 4 seconds + StomachEnterTimer = 10000; //Every 10 seconds + StomachEnterVisTimer = 0; //Always 3.5 seconds after Stomach Enter Timer + StomachEnterTarget = 0; //Target to be teleported to stomach + + //Clear players in stomach and outside + Stomach_Map.clear(); + + //Reset flags + m_creature->RemoveAurasDueToSpell(SPELL_TRANSFORM); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + + if (pInst) + pInst->SetData(DATA_CTHUN_PHASE, 0); + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + } + + void SpawnEyeTentacle(float x, float y) + { + Creature* Spawned; + Spawned = (Creature*)m_creature->SummonCreature(MOB_EYE_TENTACLE,m_creature->GetPositionX()+x,m_creature->GetPositionY()+y,m_creature->GetPositionZ(),0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,500); + if (Spawned) + { + Unit* target; + + target = SelectRandomNotStomach(); + + if (target) + Spawned->AI()->AttackStart(target); + } + } + + Unit* SelectRandomNotStomach() + { + if (Stomach_Map.empty()) + return NULL; + + HM_NAMESPACE::hash_map::iterator i = Stomach_Map.begin(); + + std::list temp; + std::list::iterator j; + + //Get all players in map + while (i != Stomach_Map.end()) + { + //Check for valid player + Unit* pUnit = Unit::GetUnit(*m_creature, i->first); + + //Only units out of stomach + if (pUnit && i->second == false) + { + temp.push_back(pUnit); + } + ++i; + } + + if (temp.empty()) + return NULL; + + j = temp.begin(); + + //Get random but only if we have more than one unit on threat list + if (temp.size() > 1) + advance ( i , rand() % (temp.size() - 1) ); + + return (*j); + } + + void UpdateAI(const uint32 diff) + { + //Check if we have a target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + { + //No target so we'll use this section to do our random wispers instance wide + //WisperTimer + if (WisperTimer < diff) + { + Map *map = m_creature->GetMap(); + if(!map->IsDungeon()) return; + + InstanceMap::PlayerList const &PlayerList = ((InstanceMap*)map)->GetPlayers(); + for (InstanceMap::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i) + { + //Play random sound to the zone + switch (rand()%8) + { + case 0: (*i)->PlaySound(RND_WISPER_1, true); break; + case 1: (*i)->PlaySound(RND_WISPER_2, true); break; + case 2: (*i)->PlaySound(RND_WISPER_3, true); break; + case 3: (*i)->PlaySound(RND_WISPER_4, true); break; + case 4: (*i)->PlaySound(RND_WISPER_5, true); break; + case 5: (*i)->PlaySound(RND_WISPER_6, true); break; + case 6: (*i)->PlaySound(RND_WISPER_7, true); break; + case 7: (*i)->PlaySound(RND_WISPER_8, true); break; + } + } + + //One random wisper every 90 - 300 seconds + WisperTimer = 90000 + (rand()% 210000); + }else WisperTimer -= diff; + + return; + } + + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0); + + //No instance + if (!pInst) + return; + + switch (pInst->GetData(DATA_CTHUN_PHASE)) + { + //Transition phase + case 2: + { + //PhaseTimer + if (PhaseTimer < diff) + { + //Switch + pInst->SetData(DATA_CTHUN_PHASE, 3); + + //Switch to c'thun model + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature, SPELL_TRANSFORM, false); + m_creature->SetHealth(m_creature->GetMaxHealth()); + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); + + //Emerging phase + //AttackStart(Unit::GetUnit(*m_creature, HoldPlayer)); + DoZoneInCombat(); + + //Place all units in threat list on outside of stomach + Stomach_Map.clear(); + + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for (; i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + //Outside stomach + Stomach_Map[(*i)->getUnitGuid()] = false; + } + + //Spawn 2 flesh tentacles + FleshTentaclesKilled = 0; + + Creature* Spawned; + + //Spawn flesh tentacle + Spawned = (Creature*)m_creature->SummonCreature(MOB_FLESH_TENTACLE, TENTACLE_POS1_X, TENTACLE_POS1_Y, TENTACLE_POS1_Z, TENTACLE_POS1_O, TEMPSUMMON_CORPSE_DESPAWN, 0); + + if (!Spawned) + FleshTentaclesKilled++; + else + ((flesh_tentacleAI*)(Spawned->AI()))->SpawnedByCthun(m_creature->GetGUID()); + + //Spawn flesh tentacle + Spawned = (Creature*)m_creature->SummonCreature(MOB_FLESH_TENTACLE, TENTACLE_POS2_X, TENTACLE_POS2_Y, TENTACLE_POS2_Z, TENTACLE_POS2_O, TEMPSUMMON_CORPSE_DESPAWN, 0); + + if (!Spawned) + FleshTentaclesKilled++; + else + ((flesh_tentacleAI*)(Spawned->AI()))->SpawnedByCthun(m_creature->GetGUID()); + + PhaseTimer = 0; + }else PhaseTimer -= diff; + + }break; + + //Body Phase + case 3: + { + //Remove Target field + m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0); + + //Weaken + if (FleshTentaclesKilled > 1) + { + pInst->SetData(DATA_CTHUN_PHASE, 4); + + DoTextEmote(EMOTE_WEAKENED, NULL); + PhaseTimer = 45000; + + DoCast(m_creature, SPELL_RED_COLORATION, true); + + HM_NAMESPACE::hash_map::iterator i = Stomach_Map.begin(); + + //Kick all players out of stomach + while (i != Stomach_Map.end()) + { + //Check for valid player + Unit* pUnit = Unit::GetUnit(*m_creature, i->first); + + //Only move units in stomach + if (pUnit && i->second == true) + { + //Teleport each player out + DoTeleportPlayer(pUnit, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()+10, rand()%6); + + //Cast knockback on them + DoCast(pUnit, SPELL_EXIT_STOMACH_KNOCKBACK, true); + + //Remove the acid debuff + pUnit->RemoveAurasDueToSpell(SPELL_DIGESTIVE_ACID); + + i->second = false; + } + ++i; + } + + return; + } + + //Stomach acid + if (StomachAcidTimer < diff) + { + //Apply aura to all players in stomach + HM_NAMESPACE::hash_map::iterator i = Stomach_Map.begin(); + + while (i != Stomach_Map.end()) + { + //Check for valid player + Unit* pUnit = Unit::GetUnit(*m_creature, i->first); + + //Only apply to units in stomach + if (pUnit && i->second == true) + { + //Cast digestive acid on them + DoCast(pUnit, SPELL_DIGESTIVE_ACID, true); + + //Check if player should be kicked from stomach + if (pUnit->GetDistance(KICK_X, KICK_Y, KICK_Z) < 15) + { + //Teleport each player out + DoTeleportPlayer(pUnit, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ()+10, rand()%6); + + //Cast knockback on them + DoCast(pUnit, SPELL_EXIT_STOMACH_KNOCKBACK, true); + + //Remove the acid debuff + pUnit->RemoveAurasDueToSpell(SPELL_DIGESTIVE_ACID); + + i->second = false; + } + } + ++i; + } + + StomachAcidTimer = 4000; + }else StomachAcidTimer -= diff; + + //Stomach Enter Timer + if (StomachEnterTimer < diff) + { + Unit* target = NULL; + target = SelectRandomNotStomach(); + + if (target) + { + //Set target in stomach + Stomach_Map[target->GetGUID()] = true; + target->InterruptNonMeleeSpells(false); + target->CastSpell(target, SPELL_MOUTH_TENTACLE, true, NULL, NULL, m_creature->GetGUID()); + StomachEnterTarget = target->GetGUID(); + StomachEnterVisTimer = 3800; + } + + StomachEnterTimer = 13800; + }else StomachEnterTimer -= diff; + + if (StomachEnterVisTimer && StomachEnterTarget) + if (StomachEnterVisTimer <= diff) + { + //Check for valid player + Unit* pUnit = Unit::GetUnit(*m_creature, StomachEnterTarget); + + if (pUnit) + { + DoTeleportPlayer(pUnit, STOMACH_X, STOMACH_Y, STOMACH_Z, STOMACH_O); + } + + StomachEnterTarget = 0; + StomachEnterVisTimer = 0; + }else StomachEnterVisTimer -= diff; + + //GientClawTentacleTimer + if (GiantClawTentacleTimer < diff) + { + Unit* target = NULL; + target = SelectRandomNotStomach(); + if (target) + { + Creature* Spawned = NULL; + + //Spawn claw tentacle on the random target + Spawned = (Creature*)m_creature->SummonCreature(MOB_GIANT_CLAW_TENTACLE,target->GetPositionX(),target->GetPositionY(),target->GetPositionZ(),0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,500); + + if (Spawned) + Spawned->AI()->AttackStart(target); + } + + //One giant claw tentacle every minute + GiantClawTentacleTimer = 60000; + }else GiantClawTentacleTimer -= diff; + + //GiantEyeTentacleTimer + if (GiantEyeTentacleTimer < diff) + { + Unit* target = NULL; + target = SelectRandomNotStomach(); + if (target) + { + + Creature* Spawned = NULL; + + //Spawn claw tentacle on the random target + Spawned = (Creature*)m_creature->SummonCreature(MOB_GIANT_EYE_TENTACLE,target->GetPositionX(),target->GetPositionY(),target->GetPositionZ(),0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,500); + + if (Spawned) + Spawned->AI()->AttackStart(target); + } + + //One giant eye tentacle every minute + GiantEyeTentacleTimer = 60000; + }else GiantEyeTentacleTimer -= diff; + + //EyeTentacleTimer + if (EyeTentacleTimer < diff) + { + //Spawn the 8 Eye Tentacles in the corret spots + SpawnEyeTentacle(0, 25); //south + SpawnEyeTentacle(12, 12); //south west + SpawnEyeTentacle(25, 0); //west + SpawnEyeTentacle(12, -12); //north west + + SpawnEyeTentacle(0, -25); //north + SpawnEyeTentacle(-12, -12); //north east + SpawnEyeTentacle(-25, 0); // east + SpawnEyeTentacle(-12, 12); // south east + + //These spawn at every 30 seconds + EyeTentacleTimer = 30000; + }else EyeTentacleTimer -= diff; + + }break; + + //Weakened state + case 4: + { + //PhaseTimer + if (PhaseTimer < diff) + { + //Switch + pInst->SetData(DATA_CTHUN_PHASE, 3); + + //Remove red coloration + m_creature->RemoveAurasDueToSpell(SPELL_RED_COLORATION); + + //Spawn 2 flesh tentacles + FleshTentaclesKilled = 0; + + Creature* Spawned; + + //Spawn flesh tentacle + Spawned = (Creature*)m_creature->SummonCreature(MOB_FLESH_TENTACLE, TENTACLE_POS1_X, TENTACLE_POS1_Y, TENTACLE_POS1_Z, TENTACLE_POS1_O, TEMPSUMMON_CORPSE_DESPAWN, 0); + + if (!Spawned) + FleshTentaclesKilled++; + else + ((flesh_tentacleAI*)(Spawned->AI()))->SpawnedByCthun(m_creature->GetGUID()); + + //Spawn flesh tentacle + Spawned = (Creature*)m_creature->SummonCreature(MOB_FLESH_TENTACLE, TENTACLE_POS2_X, TENTACLE_POS2_Y, TENTACLE_POS2_Z, TENTACLE_POS2_O, TEMPSUMMON_CORPSE_DESPAWN, 0); + + if (!Spawned) + FleshTentaclesKilled++; + else + ((flesh_tentacleAI*)(Spawned->AI()))->SpawnedByCthun(m_creature->GetGUID()); + + PhaseTimer = 0; + }else PhaseTimer -= diff; + } + } + } + + void JustDied(Unit* pKiller) + { + //Switch + if( pInst ) + pInst->SetData(DATA_CTHUN_PHASE, 5); + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + //No instance + if (!pInst) + return; + + switch (pInst->GetData(DATA_CTHUN_PHASE)) + { + case 3: + { + //Not weakened so reduce damage by 99% + if (damage / 99 > 0) damage/= 99; + else damage = 1; + + //Prevent death in non-weakened state + if (damage >= m_creature->GetHealth()) + damage = 0; + + return; + } + break; + + case 4: + { + //Weakened - takes normal damage + return; + } + + default: + damage = 0; + break; + } + } + + void FleshTentcleKilled() + { + FleshTentaclesKilled++; + } +}; + +struct MANGOS_DLL_DECL eye_tentacleAI : public Scripted_NoMovementAI +{ + eye_tentacleAI(Creature *c) : Scripted_NoMovementAI(c) + { + Reset(); + Unit* p = DoSpawnCreature(MOB_SMALL_PORTAL,0,0,0,0,TEMPSUMMON_CORPSE_DESPAWN, 0); + if (p) + Portal = p->GetGUID(); + } + + uint32 MindflayTimer; + uint32 KillSelfTimer; + uint64 Portal; + + void JustDied(Unit*) + { + Unit* p = Unit::GetUnit(*m_creature, Portal); + if (p) + p->DealDamage(p, p->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + + void Reset() + { + //Mind flay half a second after we spawn + MindflayTimer = 500; + + //This prevents eyes from overlapping + KillSelfTimer = 35000; + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + //Check if we have a target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //KillSelfTimer + if (KillSelfTimer < diff) + { + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + + return; + }else KillSelfTimer -= diff; + + //MindflayTimer + if (MindflayTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target && !target->HasAura(SPELL_DIGESTIVE_ACID, 0)) + DoCast(target,SPELL_MIND_FLAY); + + //Mindflay every 10 seconds + MindflayTimer = 10100; + }else MindflayTimer -= diff; + } +}; + +struct MANGOS_DLL_DECL claw_tentacleAI : public Scripted_NoMovementAI +{ + claw_tentacleAI(Creature *c) : Scripted_NoMovementAI(c) + { + Reset(); + Unit* p = DoSpawnCreature(MOB_SMALL_PORTAL,0,0,0,0,TEMPSUMMON_CORPSE_DESPAWN, 0); + if (p) + Portal = p->GetGUID(); + } + + uint32 GroundRuptureTimer; + uint32 HamstringTimer; + uint32 EvadeTimer; + uint64 Portal; + + void JustDied(Unit*) + { + Unit* p = Unit::GetUnit(*m_creature, Portal); + if (p) + p->DealDamage(p, p->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + + void Reset() + { + //First rupture should happen half a second after we spawn + GroundRuptureTimer = 500; + HamstringTimer = 2000; + EvadeTimer = 5000; + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + //Check if we have a target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //EvadeTimer + if (m_creature->GetDistance(m_creature->getVictim()) > ATTACK_DISTANCE) + if (EvadeTimer < diff) + { + Unit* p = Unit::GetUnit(*m_creature, Portal); + if (p) + p->DealDamage(p, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + + //Dissapear and reappear at new position + m_creature->SetVisibility(VISIBILITY_OFF); + + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (!target) + { + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + return; + } + + if (!target->HasAura(SPELL_DIGESTIVE_ACID, 0)) + { + m_creature->GetMap()->CreatureRelocation(m_creature, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0); + Unit* p = DoSpawnCreature(MOB_SMALL_PORTAL,0,0,0,0,TEMPSUMMON_CORPSE_DESPAWN, 0); + if (p) + Portal = p->GetGUID(); + + GroundRuptureTimer = 500; + HamstringTimer = 2000; + EvadeTimer = 5000; + AttackStart(target); + } + + m_creature->SetVisibility(VISIBILITY_ON); + + }else EvadeTimer -= diff; + + //GroundRuptureTimer + if (GroundRuptureTimer < diff) + { + DoCast(m_creature->getVictim(),SPELL_GROUND_RUPTURE); + GroundRuptureTimer = 30000; + }else GroundRuptureTimer -= diff; + + //HamstringTimer + if (HamstringTimer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HAMSTRING); + HamstringTimer = 5000; + }else HamstringTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL giant_claw_tentacleAI : public Scripted_NoMovementAI +{ + giant_claw_tentacleAI(Creature *c) : Scripted_NoMovementAI(c) + { + Reset(); + Unit* p = DoSpawnCreature(MOB_GIANT_PORTAL,0,0,0,0,TEMPSUMMON_CORPSE_DESPAWN, 0); + if (p) + Portal = p->GetGUID(); + } + + uint32 GroundRuptureTimer; + uint32 ThrashTimer; + uint32 HamstringTimer; + uint32 EvadeTimer; + uint64 Portal; + + void JustDied(Unit*) + { + Unit* p = Unit::GetUnit(*m_creature, Portal); + if (p) + p->DealDamage(p, p->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + + void Reset() + { + //First rupture should happen half a second after we spawn + GroundRuptureTimer = 500; + HamstringTimer = 2000; + ThrashTimer = 5000; + EvadeTimer = 5000; + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + //Check if we have a target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //EvadeTimer + if (m_creature->GetDistance(m_creature->getVictim()) > ATTACK_DISTANCE) + if (EvadeTimer < diff) + { + Unit* p = Unit::GetUnit(*m_creature, Portal); + if (p) + p->DealDamage(p, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + + //Dissapear and reappear at new position + m_creature->SetVisibility(VISIBILITY_OFF); + + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (!target) + { + m_creature->DealDamage(m_creature, m_creature->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + return; + } + + if (!target->HasAura(SPELL_DIGESTIVE_ACID, 0)) + { + m_creature->GetMap()->CreatureRelocation(m_creature, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0); + Unit* p = DoSpawnCreature(MOB_GIANT_PORTAL,0,0,0,0,TEMPSUMMON_CORPSE_DESPAWN, 0); + if (p) + Portal = p->GetGUID(); + + GroundRuptureTimer = 500; + HamstringTimer = 2000; + ThrashTimer = 5000; + EvadeTimer = 5000; + AttackStart(target); + } + + m_creature->SetVisibility(VISIBILITY_ON); + + }else EvadeTimer -= diff; + + //GroundRuptureTimer + if (GroundRuptureTimer < diff) + { + DoCast(m_creature->getVictim(),SPELL_GROUND_RUPTURE); + GroundRuptureTimer = 30000; + }else GroundRuptureTimer -= diff; + + //ThrashTimer + if (ThrashTimer < diff) + { + DoCast(m_creature->getVictim(),SPELL_THRASH); + ThrashTimer = 10000; + }else ThrashTimer -= diff; + + //HamstringTimer + if (HamstringTimer < diff) + { + DoCast(m_creature->getVictim(),SPELL_HAMSTRING); + HamstringTimer = 10000; + }else HamstringTimer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL giant_eye_tentacleAI : public Scripted_NoMovementAI +{ + giant_eye_tentacleAI(Creature *c) : Scripted_NoMovementAI(c) + { + Reset(); + Unit* p = DoSpawnCreature(MOB_GIANT_PORTAL,0,0,0,0,TEMPSUMMON_CORPSE_DESPAWN, 0); + if (p) + Portal = p->GetGUID(); + } + + uint32 BeamTimer; + uint64 Portal; + + void JustDied(Unit*) + { + Unit* p = Unit::GetUnit(*m_creature, Portal); + if (p) + p->DealDamage(p, p->GetMaxHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + } + + void Reset() + { + //Green Beam half a second after we spawn + BeamTimer = 500; + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + } + + void UpdateAI(const uint32 diff) + { + //Check if we have a target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //BeamTimer + if (BeamTimer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target && !target->HasAura(SPELL_DIGESTIVE_ACID, 0)) + DoCast(target,SPELL_GREEN_BEAM); + + //Beam every 2 seconds + BeamTimer = 2100; + }else BeamTimer -= diff; + } +}; + +//Flesh tentacle functions +void flesh_tentacleAI::UpdateAI(const uint32 diff) +{ + //Check if we have a target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (Parent) + if (CheckTimer < diff) + { + Unit* pUnit = Unit::GetUnit(*m_creature, Parent); + + if (!pUnit || !pUnit->isAlive() || !pUnit->isInCombat()) + { + Parent = 0; + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NONE, NULL, false); + return; + } + + CheckTimer = 1000; + }else CheckTimer -= diff; + + DoMeleeAttackIfReady(); +} + +void flesh_tentacleAI::JustDied(Unit* killer) +{ + if (!Parent) + { + DoYell("Error: No Parent variable", LANG_UNIVERSAL, NULL); + return; + } + + Creature* Cthun = (Creature*)Unit::GetUnit(*m_creature, Parent); + + if (Cthun) + ((cthunAI*)(Cthun->AI()))->FleshTentcleKilled(); + else DoYell("Error: No Cthun", LANG_UNIVERSAL, NULL); +} + +//GetAIs +CreatureAI* GetAI_eye_of_cthun(Creature *_Creature) +{ + return new eye_of_cthunAI (_Creature); +} + +CreatureAI* GetAI_cthun(Creature *_Creature) +{ + return new cthunAI (_Creature); +} + +CreatureAI* GetAI_eye_tentacle(Creature *_Creature) +{ + return new eye_tentacleAI (_Creature); +} + +CreatureAI* GetAI_claw_tentacle(Creature *_Creature) +{ + return new claw_tentacleAI (_Creature); +} + +CreatureAI* GetAI_giant_claw_tentacle(Creature *_Creature) +{ + return new giant_claw_tentacleAI (_Creature); +} + +CreatureAI* GetAI_giant_eye_tentacle(Creature *_Creature) +{ + return new giant_eye_tentacleAI (_Creature); +} + +CreatureAI* GetAI_flesh_tentacle(Creature *_Creature) +{ + return new flesh_tentacleAI (_Creature); +} + +void AddSC_boss_cthun() +{ + Script *newscript; + + //Eye + newscript = new Script; + newscript->Name="boss_eye_of_cthun"; + newscript->GetAI = GetAI_eye_of_cthun; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_cthun"; + newscript->GetAI = GetAI_cthun; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_eye_tentacle"; + newscript->GetAI = GetAI_eye_tentacle; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_claw_tentacle"; + newscript->GetAI = GetAI_claw_tentacle; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_giant_claw_tentacle"; + newscript->GetAI = GetAI_giant_claw_tentacle; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_giant_eye_tentacle"; + newscript->GetAI = GetAI_giant_eye_tentacle; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_giant_flesh_tentacle"; + newscript->GetAI = GetAI_flesh_tentacle; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_fankriss.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_fankriss.cpp new file mode 100644 index 00000000000..da03cc2aad5 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_fankriss.cpp @@ -0,0 +1,190 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Fankriss +SD%Complete: 100 +SDComment: sound not implemented +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +#define SOUND_SENTENCE_YOU 8588 +#define SOUND_SERVE_TO 8589 +#define SOUND_LAWS 8590 +#define SOUND_TRESPASS 8591 +#define SOUND_WILL_BE 8592 + +#define SPELL_MORTAL_WOUND 28467 +#define SPELL_ROOT 28858 + +// Enrage for his spawns +#define SPELL_ENRAGE 28798 + +struct MANGOS_DLL_DECL boss_fankrissAI : public ScriptedAI +{ + boss_fankrissAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 MortalWound_Timer; + uint32 SpawnHatchlings_Timer; + uint32 SpawnSpawns_Timer; + int Rand; + int RandX; + int RandY; + + Creature* Hatchling; + Creature* Spawn; + + void Reset() + { + MortalWound_Timer = 10000 + rand()%5000; + SpawnHatchlings_Timer = 6000 + rand()%6000; + SpawnSpawns_Timer = 15000 + rand()%30000; + } + + void SummonSpawn(Unit* victim) + { + Rand = 10 + (rand()%10); + switch (rand()%2) + { + case 0: RandX = 0 - Rand; break; + case 1: RandX = 0 + Rand; break; + } + Rand = 0; + Rand = 10 + (rand()%10); + switch (rand()%2) + { + case 0: RandY = 0 - Rand; break; + case 1: RandY = 0 + Rand; break; + } + Rand = 0; + Spawn = DoSpawnCreature(15630, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000); + if(Spawn) + ((CreatureAI*)Spawn->AI())->AttackStart(victim); + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //MortalWound_Timer + if (MortalWound_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MORTAL_WOUND); + MortalWound_Timer = 10000 + rand()%10000; + }else MortalWound_Timer -= diff; + + //Summon 1-3 Spawns of Fankriss at random time. + if (SpawnSpawns_Timer < diff) + { + switch(rand()%3) + { + case 0: + SummonSpawn(SelectUnit(SELECT_TARGET_RANDOM,0)); + break; + case 1: + SummonSpawn(SelectUnit(SELECT_TARGET_RANDOM,0)); + SummonSpawn(SelectUnit(SELECT_TARGET_RANDOM,0)); + break; + case 2: + SummonSpawn(SelectUnit(SELECT_TARGET_RANDOM,0)); + SummonSpawn(SelectUnit(SELECT_TARGET_RANDOM,0)); + SummonSpawn(SelectUnit(SELECT_TARGET_RANDOM,0)); + break; + } + SpawnSpawns_Timer = 30000 + rand()%30000; + }else SpawnSpawns_Timer -= diff; + + // Teleporting Random Target to one of the three tunnels and spawn 4 hatchlings near the gamer. + //We will only telport if fankriss has more than 3% of hp so teleported gamers can always loot. + if ( m_creature->GetHealth()*100 / m_creature->GetMaxHealth() > 3 ) + { + if(SpawnHatchlings_Timer< diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target && target->GetTypeId() == TYPEID_PLAYER) + { + DoCast(target, SPELL_ROOT); + + if(m_creature->getThreatManager().getThreat(target)) + m_creature->getThreatManager().modifyThreatPercent(target, -100); + + switch(rand()%3) + { + case 0: + DoTeleportPlayer(target, -8106.0142,1289.2900,-74.419533,5.112); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-3, target->GetPositionY()-3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-3, target->GetPositionY()+3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-5, target->GetPositionY()-5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-5, target->GetPositionY()+5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + break; + case 1: + DoTeleportPlayer(target, -7990.135354,1155.1907,-78.849319,2.608); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-3, target->GetPositionY()-3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-3, target->GetPositionY()+3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-5, target->GetPositionY()-5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-5, target->GetPositionY()+5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + break; + case 2: + DoTeleportPlayer(target,-8159.7753,1127.9064,-76.868660,0.675); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-3, target->GetPositionY()-3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-3, target->GetPositionY()+3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-5, target->GetPositionY()-5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + Hatchling = m_creature->SummonCreature(15962, target->GetPositionX()-5, target->GetPositionY()+5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + ((CreatureAI*)Hatchling->AI())->AttackStart(target); + break; + } + } + SpawnHatchlings_Timer = 45000 + rand()%15000; + }else SpawnHatchlings_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_fankriss(Creature *_Creature) +{ + return new boss_fankrissAI (_Creature); +} + +void AddSC_boss_fankriss() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_fankriss"; + newscript->GetAI = GetAI_boss_fankriss; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_huhuran.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_huhuran.cpp new file mode 100644 index 00000000000..65e1da14955 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_huhuran.cpp @@ -0,0 +1,143 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Huhuran +SD%Complete: 100 +SDComment: +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FRENZY 26051 +#define SPELL_BERSERK 26068 +#define SPELL_POISONBOLT 26052 +#define SPELL_NOXIOUSPOISON 26053 +#define SPELL_WYVERNSTING 26180 +#define SPELL_ACIDSPIT 26050 + +struct MANGOS_DLL_DECL boss_huhuranAI : public ScriptedAI +{ + boss_huhuranAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Frenzy_Timer; + uint32 Wyvern_Timer; + uint32 Spit_Timer; + uint32 PoisonBolt_Timer; + uint32 NoxiousPoison_Timer; + uint32 FrenzyBack_Timer; + + bool Frenzy; + bool Berserk; + + void Reset() + { + Frenzy_Timer = 25000 + rand()%10000; + Wyvern_Timer = 18000 + rand()%10000; + Spit_Timer = 8000; + PoisonBolt_Timer = 4000; + NoxiousPoison_Timer = 10000 + rand()%10000; + FrenzyBack_Timer = 15000; + + Frenzy = false; + Berserk = false; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Frenzy_Timer + if (!Frenzy && Frenzy_Timer < diff) + { + DoCast(m_creature, SPELL_FRENZY); + Frenzy = true; + PoisonBolt_Timer = 3000; + Frenzy_Timer = 25000 + rand()%10000; + }else Frenzy_Timer -= diff; + + // Wyvern Timer + if (Wyvern_Timer < diff) + { + if( Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0) ) + DoCast(target,SPELL_WYVERNSTING); + Wyvern_Timer = 15000 + rand()%17000; + }else Wyvern_Timer -= diff; + + //Spit Timer + if (Spit_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ACIDSPIT); + Spit_Timer = 5000 + rand()%5000; + }else Spit_Timer -= diff; + + //NoxiousPoison_Timer + if (NoxiousPoison_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_NOXIOUSPOISON); + NoxiousPoison_Timer = 12000 + rand()%12000; + }else NoxiousPoison_Timer -= diff; + + //PoisonBolt only if frenzy or berserk + if (Frenzy || Berserk) + { + if (PoisonBolt_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_POISONBOLT); + PoisonBolt_Timer = 3000; + }else PoisonBolt_Timer -= diff; + } + + //FrenzyBack_Timer + if (Frenzy && FrenzyBack_Timer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + Frenzy = false; + FrenzyBack_Timer = 15000; + }else FrenzyBack_Timer -= diff; + + if ( !Berserk && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 31 ) + { + m_creature->InterruptNonMeleeSpells(false); + DoTextEmote("is going berserk", NULL); + DoCast(m_creature, SPELL_BERSERK); + Berserk = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_huhuran(Creature *_Creature) +{ + return new boss_huhuranAI (_Creature); +} + +void AddSC_boss_huhuran() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_huhuran"; + newscript->GetAI = GetAI_boss_huhuran; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_ouro.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_ouro.cpp new file mode 100644 index 00000000000..97853c6cf87 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_ouro.cpp @@ -0,0 +1,140 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Ouro +SD%Complete: 85 +SDComment: No model for submerging. Currently just invisible. +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "def_temple_of_ahnqiraj.h" + +#define SPELL_SWEEP 26103 +#define SPELL_SANDBLAST 26102 +#define SPELL_GROUND_RUPTURE 26100 +#define SPELL_BIRTH 26262 //The Birth Animation + +#define SPELL_DIRTMOUND_PASSIVE 26092 + +struct MANGOS_DLL_DECL boss_ouroAI : public ScriptedAI +{ + boss_ouroAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Sweep_Timer; + uint32 SandBlast_Timer; + uint32 Submerge_Timer; + uint32 Back_Timer; + uint32 ChangeTarget_Timer; + uint32 Spawn_Timer; + + bool Enrage; + bool Submerged; + bool InCombat; + + void Reset() + { + Sweep_Timer = 5000 + rand()%5000; + SandBlast_Timer = 20000 + rand()%15000; + Submerge_Timer = 90000 + rand()%60000; + Back_Timer = 30000 + rand()%15000; + ChangeTarget_Timer = 5000 + rand()%3000; + Spawn_Timer = 10000 + rand()%10000; + + Enrage = false; + Submerged = false; + } + + void Aggro(Unit *who) + { + DoCast(m_creature->getVictim(), SPELL_BIRTH); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Sweep_Timer + if (!Submerged && Sweep_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SWEEP); + Sweep_Timer = 15000 + rand()%15000; + }else Sweep_Timer -= diff; + + //SandBlast_Timer + if (!Submerged && SandBlast_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SANDBLAST); + SandBlast_Timer = 20000 + rand()%15000; + }else SandBlast_Timer -= diff; + + //Submerge_Timer + if (!Submerged && Submerge_Timer < diff) + { + //Cast + m_creature->HandleEmoteCommand(EMOTE_ONESHOT_SUBMERGE); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->setFaction(35); + DoCast(m_creature, SPELL_DIRTMOUND_PASSIVE); + + Submerged = true; + Back_Timer = 30000 + rand()%15000; + }else Submerge_Timer -= diff; + + //ChangeTarget_Timer + if (Submerged && ChangeTarget_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + m_creature->Relocate(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0); + m_creature->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true,1); + + ChangeTarget_Timer = 10000 + rand()%10000; + }else ChangeTarget_Timer -= diff; + + //Back_Timer + if (Submerged && Back_Timer < diff) + { + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->setFaction(14); + + DoCast(m_creature->getVictim(), SPELL_GROUND_RUPTURE); + + Submerged = false; + Submerge_Timer = 60000 + rand()%60000; + }else Back_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ouro(Creature *_Creature) +{ + return new boss_ouroAI (_Creature); +} + +void AddSC_boss_ouro() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_ouro"; + newscript->GetAI = GetAI_boss_ouro; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_sartura.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_sartura.cpp new file mode 100644 index 00000000000..d9a9d27dffc --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_sartura.cpp @@ -0,0 +1,271 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Sartura +SD%Complete: 99 +SDComment: +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_WHIRLWIND 26083 +#define SPELL_ENRAGE 28747 //Not sure if right ID. +#define SPELL_ENRAGEHARD 28798 + +//Guard Spell +#define SPELL_WHIRLWINDADD 26038 +#define SPELL_KNOCKBACK 26027 + +struct MANGOS_DLL_DECL boss_sarturaAI : public ScriptedAI +{ + boss_sarturaAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 WhirlWind_Timer; + uint32 WhirlWindRandom_Timer; + uint32 WhirlWindEnd_Timer; + uint32 AggroReset_Timer; + uint32 AggroResetEnd_Timer; + uint32 EnrageHard_Timer; + + bool Enraged; + bool EnragedHard; + bool WhirlWind; + bool AggroReset; + + void Reset() + { + WhirlWind_Timer = 30000; + WhirlWindRandom_Timer = 3000 + rand()%4000; + WhirlWindEnd_Timer = 15000; + AggroReset_Timer = 45000 + rand()%10000; + AggroResetEnd_Timer = 5000; + EnrageHard_Timer = 10*60000; + + WhirlWind = false; + AggroReset = false; + Enraged = false; + EnragedHard = false; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (WhirlWind) + { + if (WhirlWindRandom_Timer < diff) + { + //Attack random Gamers + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,1); + if (target) + DoStartAttackAndMovement(target); + + WhirlWindRandom_Timer = 3000 + rand()%4000; + }else WhirlWindRandom_Timer -= diff; + + if (WhirlWindEnd_Timer < diff) + { + WhirlWind = false; + WhirlWind_Timer = 25000 + rand()%15000; + }else WhirlWindEnd_Timer -= diff; + } + + if (!WhirlWind) + { + if (WhirlWind_Timer < diff) + { + DoCast(m_creature, SPELL_WHIRLWIND); + WhirlWind = true; + WhirlWindEnd_Timer = 15000; + }else WhirlWind_Timer -= diff; + + if (AggroReset_Timer < diff) + { + //Attack random Gamers + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,1); + if (target) + m_creature->TauntApply(target); + + AggroReset = true; + AggroReset_Timer = 2000 + rand()%3000; + }else AggroReset_Timer -= diff; + + if (AggroReset) + { + if (AggroResetEnd_Timer GetHealth()*100 / m_creature->GetMaxHealth() <= 20 && !m_creature->IsNonMeleeSpellCasted(false)) + { + DoCast(m_creature, SPELL_ENRAGE); + Enraged = true; + } + } + + //After 10 minutes hard enrage + if (!EnragedHard) + { + if (EnrageHard_Timer < diff) + { + DoCast(m_creature, SPELL_ENRAGEHARD); + EnragedHard = true; + }EnrageHard_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } + } +}; + +struct MANGOS_DLL_DECL mob_sartura_royal_guardAI : public ScriptedAI +{ + mob_sartura_royal_guardAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 WhirlWind_Timer; + uint32 WhirlWindRandom_Timer; + uint32 WhirlWindEnd_Timer; + uint32 AggroReset_Timer; + uint32 AggroResetEnd_Timer; + uint32 KnockBack_Timer; + + bool WhirlWind; + bool AggroReset; + + void Reset() + { + WhirlWind_Timer = 30000; + WhirlWindRandom_Timer = 3000 + rand()%4000; + WhirlWindEnd_Timer = 15000; + AggroReset_Timer = 45000 + rand()%10000; + AggroResetEnd_Timer = 5000; + KnockBack_Timer = 10000; + + WhirlWind = false; + AggroReset = false; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (!WhirlWind && WhirlWind_Timer < diff) + { + DoCast(m_creature, SPELL_WHIRLWINDADD); + WhirlWind = true; + WhirlWind_Timer = 25000 + rand()%15000; + WhirlWindEnd_Timer = 15000; + }else WhirlWind_Timer -= diff; + + if (WhirlWind) + { + if (WhirlWindRandom_Timer < diff) + { + //Attack random Gamers + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,1); + if (target) + m_creature->TauntApply(target); + + WhirlWindRandom_Timer = 3000 + rand()%4000; + }else WhirlWindRandom_Timer -= diff; + + if (WhirlWindEnd_Timer < diff) + { + WhirlWind = false; + }else WhirlWindEnd_Timer -= diff; + } + + if (!WhirlWind) + { + if(AggroReset_Timer < diff) + { + //Attack random Gamers + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,1); + if (target) + DoStartAttackAndMovement(target); + + AggroReset = true; + AggroReset_Timer = 2000 + rand()%3000; + }else AggroReset_Timer -= diff; + + if (KnockBack_Timer < diff) + { + DoCast(m_creature, SPELL_WHIRLWINDADD); + KnockBack_Timer = 10000 + rand()%10000; + }else KnockBack_Timer -= diff; + } + + if (AggroReset) + { + if (AggroResetEnd_Timer Name="boss_sartura"; + newscript->GetAI = GetAI_boss_sartura; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_sartura_royal_guard"; + newscript->GetAI = GetAI_mob_sartura_royal_guard; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_skeram.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_skeram.cpp new file mode 100644 index 00000000000..a2123c21da0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_skeram.cpp @@ -0,0 +1,314 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Skeram +SD%Complete: 75 +SDComment: Mind Control buggy. +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "def_temple_of_ahnqiraj.h" +#include "Group.h" + +#define SPELL_ARCANE_EXPLOSION 25679 +#define SPELL_EARTH_SHOCK 26194 +#define SPELL_TRUE_FULFILLMENT4 26526 +#define SPELL_BLINK 28391 + +#define SOUND_AGGRO1 8615 //8615 Are you so eager to die? I would be happy to accomodate you. +#define SOUND_AGGRO2 8616 //8616 Cower mortals! The age of darkness is at hand. +#define SOUND_AGGRO3 8621 //8621 Tremble! The end is upon you. +#define SOUND_SLAY1 8617 //8617 Let your death serve as an example! +#define SOUND_SLAY2 8619 //8619 Spineless wretchers you will drown in rivers of blood! +#define SOUND_SLAY3 8620 //8620 The screams of the dying will fill the air. A symphony of terror is about to begin! +#define SOUND_SPLIT 8618 //8618 Prepare for the return of the ancient ones! +#define SOUND_DEATH 8622 //8622 You only delay... the inevetable + +class ov_mycoordinates +{ + public: + float x,y,z,r; + ov_mycoordinates(float cx, float cy, float cz, float cr) + { + x = cx; y = cy; z = cz; r = cr; + } +}; + +struct MANGOS_DLL_DECL boss_skeramAI : public ScriptedAI +{ + boss_skeramAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + IsImage = false; + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 ArcaneExplosion_Timer; + uint32 EarthShock_Timer; + uint32 FullFillment_Timer; + uint32 Blink_Timer; + uint32 Invisible_Timer; + + Creature *Image1, *Image2; + + bool Images75; + bool Images50; + bool Images25; + bool IsImage; + bool Invisible; + + void Reset() + { + ArcaneExplosion_Timer = 6000 + rand()%6000; + EarthShock_Timer = 2000; + FullFillment_Timer = 15000; + Blink_Timer = 8000 + rand()%12000; + Invisible_Timer = 500; + + Images75 = false; + Images50 = false; + Images25 = false; + Invisible = false; + + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetVisibility(VISIBILITY_ON); + + if (IsImage) + m_creature->setDeathState(JUST_DIED); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%3) + { + case 0: + DoPlaySoundToSet(m_creature,SOUND_SLAY1); + break; + case 1: + DoPlaySoundToSet(m_creature,SOUND_SLAY2); + break; + case 2: + DoPlaySoundToSet(m_creature,SOUND_SLAY3); + break; + } + } + + void JustDied(Unit* Killer) + { + if (!IsImage) + DoPlaySoundToSet(m_creature,SOUND_DEATH); + } + + void Aggro(Unit *who) + { + if (IsImage || Images75) + return; + switch(rand()%3) + { + case 0: + DoPlaySoundToSet(m_creature,SOUND_AGGRO1); + break; + case 1: + DoPlaySoundToSet(m_creature,SOUND_AGGRO2); + break; + case 2: + DoPlaySoundToSet(m_creature,SOUND_AGGRO3); + break; + } + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //ArcaneExplosion_Timer + if (ArcaneExplosion_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_ARCANE_EXPLOSION); + ArcaneExplosion_Timer = 8000 + rand()%10000; + }else ArcaneExplosion_Timer -= diff; + + //If we are within range melee the target + if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) + { + //Make sure our attack is ready and we arn't currently casting + if( m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false)) + { + m_creature->AttackerStateUpdate(m_creature->getVictim()); + m_creature->resetAttackTimer(); + } + }else + { + //EarthShock_Timer + if (EarthShock_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_EARTH_SHOCK); + EarthShock_Timer = 1000; + }else EarthShock_Timer -= diff; + } + + //Blink_Timer + if (Blink_Timer < diff) + { + //DoCast(m_creature, SPELL_BLINK); + switch(rand()%3) + { + case 0: + m_creature->Relocate(-8340.782227,2083.814453,125.648788,0); + DoResetThreat(); + break; + case 1: + m_creature->Relocate(-8341.546875,2118.504639,133.058151,0); + DoResetThreat(); + break; + case 2: + m_creature->Relocate(-8318.822266,2058.231201,133.058151,0); + DoResetThreat(); + break; + } + DoStopAttack(); + + Blink_Timer= 20000 + rand()%20000; + }else Blink_Timer -= diff; + + int procent = (int) (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() +0.5); + + //Summoning 2 Images and teleporting to a random position on 75% health + if ( (!Images75 && !IsImage) && + (procent <= 75 && procent > 70) ) + DoSplit(75); + + //Summoning 2 Images and teleporting to a random position on 50% health + if ( (!Images50 && !IsImage) && + (procent <= 50 && procent > 45) ) + DoSplit(50); + + //Summoning 2 Images and teleporting to a random position on 25% health + if ( (!Images25 && !IsImage) && + (procent <= 25 && procent > 20) ) + DoSplit(25); + + //Invisible_Timer + if (Invisible) + { + if (Invisible_Timer < diff) + { + //Making Skeram visible after telporting + m_creature->SetVisibility(VISIBILITY_ON); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + Invisible_Timer = 2500; + Invisible = false; + }else Invisible_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } + + void DoSplit(int atPercent /* 75 50 25 */) + { + DoPlaySoundToSet(m_creature,SOUND_SPLIT); + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + ov_mycoordinates *place1 = new ov_mycoordinates(-8340.782227,2083.814453,125.648788,0); + ov_mycoordinates *place2 = new ov_mycoordinates(-8341.546875,2118.504639,133.058151,0); + ov_mycoordinates *place3 = new ov_mycoordinates(-8318.822266,2058.231201,133.058151,0); + + ov_mycoordinates *bossc=place1, *i1=place2, *i2=place3; + + switch(rand()%3) + { + case 0: + bossc=place1;i1=place2;i2=place3; + break; + case 1: + bossc=place2;i1=place1;i2=place3; + break; + case 2: + bossc=place3;i1=place1;i2=place2; + break; + } + + for (int tryi = 0; tryi < 41; tryi ++) + { + Unit *targetpl = SelectUnit(SELECT_TARGET_RANDOM, 0); + if (targetpl->isType(TYPEMASK_PLAYER)) + { + Group *grp = ((Player *)targetpl)->GetGroup(); + if (grp) + { + for (int ici = 0; ici < TARGETICONCOUNT; ici++) + { + //if (grp ->m_targetIcons[ici] == m_creature->GetGUID()) -- private member:( + grp->SetTargetIcon(ici, 0); + } + } + break; + } + } + + m_creature->RemoveAllAuras(); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetVisibility(VISIBILITY_OFF); + m_creature->Relocate(bossc->x, bossc->y, bossc->z, bossc->r); + Invisible = true; + DoResetThreat(); + DoStopAttack(); + + switch (atPercent) + { + case 75: Images75 = true; break; + case 50: Images50 = true; break; + case 25: Images25 = true; break; + } + + Image1 = m_creature->SummonCreature(15263, i1->x, i1->y, i1->z, i1->r, TEMPSUMMON_CORPSE_DESPAWN, 30000); + Image1->SetMaxHealth(m_creature->GetMaxHealth() / 5); + Image1->SetHealth(m_creature->GetHealth() / 5); + Image1->AI()->AttackStart(target); + + Image2 = m_creature->SummonCreature(15263,i2->x, i2->y, i2->z, i2->r, TEMPSUMMON_CORPSE_DESPAWN, 30000); + Image2->SetMaxHealth(m_creature->GetMaxHealth() / 5); + Image2->SetHealth(m_creature->GetHealth() / 5); + Image2->AI()->AttackStart(target); + + ((boss_skeramAI*)Image1->AI())->IsImage = true; + ((boss_skeramAI*)Image2->AI())->IsImage = true; + + Invisible = true; + } +}; + +CreatureAI* GetAI_boss_skeram(Creature *_Creature) +{ + return new boss_skeramAI (_Creature); +} + +void AddSC_boss_skeram() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_skeram"; + newscript->GetAI = GetAI_boss_skeram; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp new file mode 100644 index 00000000000..dacb55c4055 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp @@ -0,0 +1,698 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Twinemperors +SD%Complete: 95 +SDComment: +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "def_temple_of_ahnqiraj.h" +#include "WorldPacket.h" + +#include "Item.h" +#include "Spell.h" + +#define SPELL_HEAL_BROTHER 7393 +#define SPELL_TWIN_TELEPORT 800 // CTRA watches for this spell to start its teleport timer +#define SPELL_TWIN_TELEPORT_VISUAL 26638 // visual + +#define SPELL_EXPLODEBUG 804 +#define SPELL_MUTATE_BUG 802 + +#define SOUND_VN_DEATH 8660 //8660 - Death - Feel +#define SOUND_VN_AGGRO 8661 //8661 - Aggro - Let none +#define SOUND_VN_KILL 8662 //8661 - Kill - your fate + +#define SOUND_VL_AGGRO 8657 //8657 - Aggro - To Late +#define SOUND_VL_KILL 8658 //8658 - Kill - You will not +#define SOUND_VL_DEATH 8659 //8659 - Death + +#define PULL_RANGE 50 +#define ABUSE_BUG_RANGE 20 +#define SPELL_BERSERK 26662 +#define TELEPORTTIME 30000 + +#define SPELL_UPPERCUT 26007 +#define SPELL_UNBALANCING_STRIKE 26613 + +#define VEKLOR_DIST 20 // VL will not come to melee when attacking + +#define SPELL_SHADOWBOLT 26006 +#define SPELL_BLIZZARD 26607 +#define SPELL_ARCANEBURST 568 + +struct MANGOS_DLL_DECL boss_twinemperorsAI : public ScriptedAI +{ + ScriptedInstance *pInstance; + uint32 Heal_Timer; + uint32 Teleport_Timer; + bool AfterTeleport; + uint32 AfterTeleportTimer; + bool DontYellWhenDead; + uint32 Abuse_Bug_Timer, BugsTimer; + bool tspellcasted; + uint32 EnrageTimer; + + virtual bool IAmVeklor() = 0; + virtual void Reset() = 0; + virtual void CastSpellOnBug(Creature *target) = 0; + + boss_twinemperorsAI(Creature *c): ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + } + + void TwinReset() + { + Heal_Timer = 0; // first heal immediately when they get close together + Teleport_Timer = TELEPORTTIME; + AfterTeleport = false; + tspellcasted = false; + AfterTeleportTimer = 0; + Abuse_Bug_Timer = 10000 + rand()%7000; + BugsTimer = 2000; + m_creature->clearUnitState(UNIT_STAT_STUNDED); + DontYellWhenDead = false; + EnrageTimer = 15*60000; + } + + Creature *GetOtherBoss() + { + if(pInstance) + { + return (Creature *)Unit::GetUnit((*m_creature), pInstance->GetData64(IAmVeklor() ? DATA_VEKNILASH : DATA_VEKLOR)); + } + else + { + return (Creature *)0; + } + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + Unit *pOtherBoss = GetOtherBoss(); + if (pOtherBoss) + { + float dPercent = ((float)damage) / ((float)m_creature->GetMaxHealth()); + int odmg = (int)(dPercent * ((float)pOtherBoss->GetMaxHealth())); + int ohealth = pOtherBoss->GetHealth()-odmg; + pOtherBoss->SetHealth(ohealth > 0 ? ohealth : 0); + if (ohealth <= 0) + { + pOtherBoss->setDeathState(JUST_DIED); + pOtherBoss->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + } + } + } + + void JustDied(Unit* Killer) + { + Creature *pOtherBoss = GetOtherBoss(); + if (pOtherBoss) + { + pOtherBoss->SetHealth(0); + pOtherBoss->setDeathState(JUST_DIED); + pOtherBoss->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + ((boss_twinemperorsAI *)pOtherBoss->AI())->DontYellWhenDead = true; + } + if (!DontYellWhenDead) // I hope AI is not threaded + DoPlaySoundToSet(m_creature, IAmVeklor() ? SOUND_VL_DEATH : SOUND_VN_DEATH); + } + + void KilledUnit(Unit* victim) + { + DoPlaySoundToSet(m_creature, IAmVeklor() ? SOUND_VL_KILL : SOUND_VN_KILL); + } + + void Aggro(Unit *who) + { + DoZoneInCombat(); + InCombat = true; + Creature *pOtherBoss = GetOtherBoss(); + if (pOtherBoss) + { + // TODO: we should activate the other boss location so he can start attackning even if nobody + // is near I dont know how to do that + ScriptedAI *otherAI = (ScriptedAI*)pOtherBoss->AI(); + if (!otherAI->InCombat) + { + DoPlaySoundToSet(m_creature, IAmVeklor() ? SOUND_VL_AGGRO : SOUND_VN_AGGRO); + otherAI->AttackStart(who); + otherAI->DoZoneInCombat(); + } + } + } + + void SpellHit(Unit *caster, const SpellEntry *entry) + { + if (caster == m_creature) + return; + + Creature *pOtherBoss = GetOtherBoss(); + if (entry->Id != SPELL_HEAL_BROTHER || !pOtherBoss) + return; + + // add health so we keep same percentage for both brothers + uint32 mytotal = m_creature->GetMaxHealth(), histotal = pOtherBoss->GetMaxHealth(); + float mult = ((float)mytotal) / ((float)histotal); + if (mult < 1) + mult = 1.0f/mult; + #define HEAL_BROTHER_AMOUNT 30000.0f + uint32 largerAmount = (uint32)((HEAL_BROTHER_AMOUNT * mult) - HEAL_BROTHER_AMOUNT); + + uint32 myh = m_creature->GetHealth(); + uint32 hish = pOtherBoss->GetHealth(); + if (mytotal > histotal) + { + uint32 h = m_creature->GetHealth()+largerAmount; + m_creature->SetHealth(std::min(mytotal, h)); + } + else + { + uint32 h = pOtherBoss->GetHealth()+largerAmount; + pOtherBoss->SetHealth(std::min(histotal, h)); + } + } + + void TryHealBrother(uint32 diff) + { + if (IAmVeklor()) // this spell heals caster and the other brother so let VN cast it + return; + + if (Heal_Timer < diff) + { + Unit *pOtherBoss = GetOtherBoss(); + if (pOtherBoss && (pOtherBoss->GetDistance((const Creature *)m_creature) <= 60)) + { + DoCast(pOtherBoss, SPELL_HEAL_BROTHER); + Heal_Timer = 1000; + } + } else Heal_Timer -= diff; + } + + Unit *GetAnyoneCloseEnough(float dist, bool totallyRandom) + { + int cnt = 0; + std::list::iterator i; + std::list candidates; + + for (i = m_creature->getThreatManager().getThreatList().begin();i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if (m_creature->IsWithinDistInMap(pUnit, dist)) + { + if (!totallyRandom) + return pUnit; + candidates.push_back((*i)); + cnt ++; + } + } + if (!cnt) + return NULL; + for (int randomi = rand() % cnt; randomi > 0; randomi --) + candidates.pop_front(); + + i = candidates.begin(); + Unit *ret = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + candidates.clear(); + return ret; + } + + Unit *PickNearestPlayer() + { + Unit *nearp = NULL; + float neardist = 0.0f; + std::list::iterator i; + for (i = m_creature->getThreatManager().getThreatList().begin();i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + Unit* pUnit = NULL; + pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if (!pUnit) + continue; + float pudist = pUnit->GetDistance((const Creature *)m_creature); + if (!nearp || (neardist > pudist)) + { + nearp = pUnit; + neardist = pudist; + } + } + return nearp; + } + + void TeleportToMyBrother() + { + if (!pInstance) + return; + + Teleport_Timer = TELEPORTTIME; + + if(IAmVeklor()) + return; // mechanics handled by veknilash so they teleport exactly at the same time and to correct coordinates + + Creature *pOtherBoss = GetOtherBoss(); + if (pOtherBoss) + { + //m_creature->MonsterYell("Teleporting ...", LANG_UNIVERSAL, 0); + float other_x = pOtherBoss->GetPositionX(); + float other_y = pOtherBoss->GetPositionY(); + float other_z = pOtherBoss->GetPositionZ(); + float other_o = pOtherBoss->GetOrientation(); + + Map *thismap = m_creature->GetMap(); + thismap->CreatureRelocation(pOtherBoss, m_creature->GetPositionX(), + m_creature->GetPositionY(), m_creature->GetPositionZ(), m_creature->GetOrientation()); + thismap->CreatureRelocation(m_creature, other_x, other_y, other_z, other_o); + + SetAfterTeleport(); + ((boss_twinemperorsAI*) pOtherBoss->AI())->SetAfterTeleport(); + } + } + + void SetAfterTeleport() + { + m_creature->InterruptNonMeleeSpells(false); + DoStopAttack(); + DoResetThreat(); + DoCast(m_creature, SPELL_TWIN_TELEPORT_VISUAL); + m_creature->addUnitState(UNIT_STAT_STUNDED); + AfterTeleport = true; + AfterTeleportTimer = 2000; + tspellcasted = false; + } + + bool TryActivateAfterTTelep(uint32 diff) + { + if (AfterTeleport) + { + if (!tspellcasted) + { + m_creature->clearUnitState(UNIT_STAT_STUNDED); + DoCast(m_creature, SPELL_TWIN_TELEPORT); + m_creature->addUnitState(UNIT_STAT_STUNDED); + } + + tspellcasted = true; + + if (AfterTeleportTimer < diff) + { + AfterTeleport = false; + m_creature->clearUnitState(UNIT_STAT_STUNDED); + Unit *nearu = PickNearestPlayer(); + //DoYell(nearu->GetName(), LANG_UNIVERSAL, 0); + AttackStart(nearu); + m_creature->getThreatManager().addThreat(nearu, 10000); + return true; + } + else + { + AfterTeleportTimer -= diff; + // update important timers which would otherwise get skipped + if (EnrageTimer > diff) + EnrageTimer -= diff; + else + EnrageTimer = 0; + if (Teleport_Timer > diff) + Teleport_Timer -= diff; + else + Teleport_Timer = 0; + return false; + } + } + else + { + return true; + } + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (attackRadius < PULL_RANGE) + attackRadius = PULL_RANGE; + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= /*CREATURE_Z_ATTACK_RANGE*/7 /*there are stairs*/) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + AttackStart(who); + } + } + } + + class AnyBugCheck + { + public: + AnyBugCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {} + bool operator()(Unit* u) + { + Creature *c = (Creature *)u; + if (!i_obj->IsWithinDistInMap(c, i_range)) + return false; + return (c->GetEntry() == 15316 || c->GetEntry() == 15317); + } + private: + WorldObject const* i_obj; + float i_range; + }; + + Creature *RespawnNearbyBugsAndGetOne() + { + CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + std::list unitList; + + AnyBugCheck u_check(m_creature, 150); + MaNGOS::CreatureListSearcher searcher(unitList, u_check); + TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_creature_searcher, *(m_creature->GetMap())); + + Creature *nearb = NULL; + + for(std::list::iterator iter = unitList.begin(); iter != unitList.end(); ++iter) + { + Creature *c = (Creature *)(*iter); + if (c->isDead()) + { + c->Respawn(); + c->setFaction(7); + c->RemoveAllAuras(); + } + if (c->IsWithinDistInMap(m_creature, ABUSE_BUG_RANGE)) + { + if (!nearb || (rand()%4)==0) + nearb = c; + } + } + return nearb; + } + + void HandleBugs(uint32 diff) + { + if (BugsTimer < diff || Abuse_Bug_Timer < diff) + { + Creature *c = RespawnNearbyBugsAndGetOne(); + if (Abuse_Bug_Timer < diff) + { + if (c) + { + CastSpellOnBug(c); + Abuse_Bug_Timer = 10000 + rand()%7000; + } + else + { + Abuse_Bug_Timer = 1000; + } + } + else + { + Abuse_Bug_Timer -= diff; + } + BugsTimer = 2000; + } + else + { + BugsTimer -= diff; + Abuse_Bug_Timer -= diff; + } + } + + void CheckEnrage(uint32 diff) + { + if (EnrageTimer < diff) + { + if (!m_creature->IsNonMeleeSpellCasted(true)) + { + DoCast(m_creature, SPELL_BERSERK); + EnrageTimer = 60*60000; + } else EnrageTimer = 0; + } else EnrageTimer-=diff; + } +}; + +class MANGOS_DLL_DECL BugAura : public Aura +{ + public: + BugAura(SpellEntry *spell, uint32 eff, int32 *bp, Unit *target, Unit *caster) : Aura(spell, eff, bp, target, caster, NULL) + {} +}; + +struct MANGOS_DLL_DECL boss_veknilashAI : public boss_twinemperorsAI +{ + bool IAmVeklor() {return false;} + boss_veknilashAI(Creature *c) : boss_twinemperorsAI(c) + { + Reset(); + } + + uint32 UpperCut_Timer; + uint32 UnbalancingStrike_Timer; + uint32 Scarabs_Timer; + int Rand; + int RandX; + int RandY; + + Creature* Summoned; + + void Reset() + { + TwinReset(); + UpperCut_Timer = 14000 + rand()%15000; + UnbalancingStrike_Timer = 8000 + rand()%10000; + Scarabs_Timer = 7000 + rand()%7000; + + //Added. Can be removed if its included in DB. + m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, true); + } + + void CastSpellOnBug(Creature *target) + { + target->setFaction(14); + ((CreatureAI*)target->AI())->AttackStart(m_creature->getThreatManager().getHostilTarget()); + SpellEntry *spell = (SpellEntry *)GetSpellStore()->LookupEntry(SPELL_MUTATE_BUG); + for (int i=0; i<3; i++) + { + if (!spell->Effect[i]) + continue; + target->AddAura(new BugAura(spell, i, NULL, target, target)); + } + target->SetHealth(target->GetMaxHealth()); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (!TryActivateAfterTTelep(diff)) + return; + + //UnbalancingStrike_Timer + if (UnbalancingStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_UNBALANCING_STRIKE); + UnbalancingStrike_Timer = 8000+rand()%12000; + }else UnbalancingStrike_Timer -= diff; + + if (UpperCut_Timer < diff) + { + Unit* randomMelee = GetAnyoneCloseEnough(ATTACK_DISTANCE, true); + if (randomMelee) + DoCast(randomMelee,SPELL_UPPERCUT); + UpperCut_Timer = 15000+rand()%15000; + }else UpperCut_Timer -= diff; + + HandleBugs(diff); + + //Heal brother when 60yrds close + TryHealBrother(diff); + + //Teleporting to brother + if(Teleport_Timer < diff) + { + TeleportToMyBrother(); + }else Teleport_Timer -= diff; + + CheckEnrage(diff); + + DoMeleeAttackIfReady(); + } +}; + +struct MANGOS_DLL_DECL boss_veklorAI : public boss_twinemperorsAI +{ + bool IAmVeklor() {return true;} + boss_veklorAI(Creature *c) : boss_twinemperorsAI(c) + { + Reset(); + } + + uint32 ShadowBolt_Timer; + uint32 Blizzard_Timer; + uint32 ArcaneBurst_Timer; + uint32 Scorpions_Timer; + int Rand; + int RandX; + int RandY; + + Creature* Summoned; + + void Reset() + { + TwinReset(); + ShadowBolt_Timer = 0; + Blizzard_Timer = 15000 + rand()%5000;; + ArcaneBurst_Timer = 1000; + Scorpions_Timer = 7000 + rand()%7000; + + //Added. Can be removed if its included in DB. + m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, 0); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, 0); + } + + void CastSpellOnBug(Creature *target) + { + target->setFaction(14); + SpellEntry *spell = (SpellEntry *)GetSpellStore()->LookupEntry(SPELL_EXPLODEBUG); + for (int i=0; i<3; i++) + { + if (!spell->Effect[i]) + continue; + target->AddAura(new BugAura(spell, i, NULL, target, target)); + } + target->SetHealth(target->GetMaxHealth()); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + // reset arcane burst after teleport - we need to do this because + // when VL jumps to VN's location there will be a warrior who will get only 2s to run away + // which is almost impossible + if (AfterTeleport) + ArcaneBurst_Timer = 5000; + if (!TryActivateAfterTTelep(diff)) + return; + + //ShadowBolt_Timer + if (ShadowBolt_Timer < diff) + { + if (m_creature->GetDistance(m_creature->getVictim()) > 45) + m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), VEKLOR_DIST, 0); + else + DoCast(m_creature->getVictim(),SPELL_SHADOWBOLT); + ShadowBolt_Timer = 2000; + }else ShadowBolt_Timer -= diff; + + //Blizzard_Timer + if (Blizzard_Timer < diff) + { + Unit* target = NULL; + target = GetAnyoneCloseEnough(45, true); + if (target) + DoCast(target,SPELL_BLIZZARD); + Blizzard_Timer = 15000+rand()%15000; + }else Blizzard_Timer -= diff; + + if (ArcaneBurst_Timer < diff) + { + Unit *mvic; + if ((mvic=GetAnyoneCloseEnough(ATTACK_DISTANCE, false))!=NULL) + { + DoCast(mvic,SPELL_ARCANEBURST); + ArcaneBurst_Timer = 5000; + } + }else ArcaneBurst_Timer -= diff; + + HandleBugs(diff); + + //Heal brother when 60yrds close + TryHealBrother(diff); + + //Teleporting to brother + if(Teleport_Timer < diff) + { + TeleportToMyBrother(); + }else Teleport_Timer -= diff; + + CheckEnrage(diff); + + //VL doesn't melee + //DoMeleeAttackIfReady(); + } + + void AttackStart(Unit* who) + { + if (!who) + return; + + if (who->isTargetableForAttack()) + { + // VL doesn't melee + if ( m_creature->Attack(who, false) ) + { + m_creature->GetMotionMaster()->MoveChase(who, VEKLOR_DIST, 0); + m_creature->AddThreat(who, 0.0f); + } + + if (!InCombat) + { + Aggro(who); + InCombat = true; + } + } + } +}; + +CreatureAI* GetAI_boss_veknilash(Creature *_Creature) +{ + return new boss_veknilashAI (_Creature); +} + +CreatureAI* GetAI_boss_veklor(Creature *_Creature) +{ + return new boss_veklorAI (_Creature); +} + +void AddSC_boss_twinemperors() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_veknilash"; + newscript->GetAI = GetAI_boss_veknilash; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="boss_veklor"; + newscript->GetAI = GetAI_boss_veklor; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_viscidus.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_viscidus.cpp new file mode 100644 index 00000000000..36a8bfea1c1 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_viscidus.cpp @@ -0,0 +1,29 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Viscidus +SD%Complete: 0 +SDComment: place holder +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_POISON_SHOCK 25993 +#define SPELL_POISONBOLT_VOLLEY 25991 + +#define SPELL_TOXIN_CLOUD 25989 diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/def_temple_of_ahnqiraj.h b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/def_temple_of_ahnqiraj.h new file mode 100644 index 00000000000..fdc6d30a0cb --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/def_temple_of_ahnqiraj.h @@ -0,0 +1,22 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_TEMPLE_OF_AHNQIRAJ_H +#define DEF_TEMPLE_OF_AHNQIRAJ_H + +#define DATA_SKERAM 1 +#define DATA_KRI 2 +#define DATA_VEM 3 +#define DATA_VEMISDEAD 4 +#define DATA_VEM_DEATH 5 +#define DATA_VEKLOR 6 +#define DATA_VEKLORISDEAD 7 +#define DATA_VEKLOR_DEATH 8 +#define DATA_VEKNILASH 9 +#define DATA_VEKNILASHISDEAD 10 +#define DATA_VEKNILASH_DEATH 11 +#define DATA_BUG_TRIO_DEATH 14 + +#define DATA_CTHUN_PHASE 20 +#endif diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp new file mode 100644 index 00000000000..724a64e2baa --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp @@ -0,0 +1,165 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Temple_of_Ahnqiraj +SD%Complete: 80 +SDComment: +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "def_temple_of_ahnqiraj.h" + +struct MANGOS_DLL_DECL instance_temple_of_ahnqiraj : public ScriptedInstance +{ + instance_temple_of_ahnqiraj(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + //If Vem is dead... + bool IsBossDied[3]; + + //Storing Skeram, Vem and Kri. + uint64 SkeramGUID; + uint64 VemGUID; + uint64 KriGUID; + uint64 VeklorGUID; + uint64 VeknilashGUID; + + uint32 BugTrioDeathCount; + + uint32 CthunPhase; + + void Initialize() + { + IsBossDied[0] = false; + IsBossDied[1] = false; + IsBossDied[2] = false; + + SkeramGUID = 0; + VemGUID = 0; + KriGUID = 0; + VeklorGUID = 0; + VeknilashGUID = 0; + + BugTrioDeathCount = 0; + + CthunPhase = 0; + } + + void OnCreatureCreate (Creature *creature, uint32 creature_entry) + { + switch (creature_entry) + { + case 15263: SkeramGUID = creature->GetGUID(); break; + case 15544: VemGUID = creature->GetGUID(); break; + case 15511: KriGUID = creature->GetGUID(); break; + case 15276: VeklorGUID = creature->GetGUID(); break; + case 15275: VeknilashGUID = creature->GetGUID(); break; + } + } + + bool IsEncounterInProgress() const + { + //not active in AQ40 + return false; + } + + uint32 GetData(uint32 type) + { + switch(type) + { + case DATA_VEMISDEAD: + if(IsBossDied[0]) + return 1; + break; + + case DATA_VEKLORISDEAD: + if(IsBossDied[1]) + return 1; + break; + + case DATA_VEKNILASHISDEAD: + if(IsBossDied[2]) + return 1; + break; + + case DATA_BUG_TRIO_DEATH: + return BugTrioDeathCount; + + case DATA_CTHUN_PHASE: + return CthunPhase; + } + return 0; + } + + uint64 GetData64 (uint32 identifier) + { + switch(identifier) + { + case DATA_SKERAM: + return SkeramGUID; + case DATA_VEM: + return VemGUID; + case DATA_KRI: + return KriGUID; + case DATA_VEKLOR: + return VeklorGUID; + case DATA_VEKNILASH: + return VeknilashGUID; + } + return 0; + } // end GetData64 + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_VEM_DEATH: + IsBossDied[0] = true; + break; + + case DATA_BUG_TRIO_DEATH: + BugTrioDeathCount++; + break; + + case DATA_VEKLOR_DEATH: + IsBossDied[1] = true; + break; + + case DATA_VEKNILASH_DEATH: + IsBossDied[2] = true; + break; + + case DATA_CTHUN_PHASE: + CthunPhase = data; + break; + } + } +}; + +InstanceData* GetInstanceData_instance_temple_of_ahnqiraj(Map* map) +{ + return new instance_temple_of_ahnqiraj(map); +} + +void AddSC_instance_temple_of_ahnqiraj() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_temple_of_ahnqiraj"; + newscript->GetInstanceData = GetInstanceData_instance_temple_of_ahnqiraj; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp new file mode 100644 index 00000000000..62c21978adf --- /dev/null +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp @@ -0,0 +1,344 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: mob_anubisath_sentinel +SD%Complete: 95 +SDComment: Shadow storm is not properly implemented in core it should only target ppl outside of melee range. +SDCategory: Temple of Ahn'Qiraj +EndScriptData */ + +#include "precompiled.h" +#include "WorldPacket.h" + +#include "Item.h" +#include "Player.h" +#include "Spell.h" + +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" + +#define SPELL_MENDING_BUFF 2147 + +#define SPELL_KNOCK_BUFF 21737 +#define SPELL_KNOCK 25778 +#define SPELL_MANAB_BUFF 812 +#define SPELL_MANAB 25779 + +#define SPELL_REFLECTAF_BUFF 13022 +#define SPELL_REFLECTSFr_BUFF 19595 +#define SPELL_THORNS_BUFF 25777 + +#define SPELL_THUNDER_BUFF 2834 +#define SPELL_THUNDER 8732 + +#define SPELL_MSTRIKE_BUFF 9347 +#define SPELL_MSTRIKE 24573 + +#define SPELL_STORM_BUFF 2148 +#define SPELL_STORM 26546 + +class NearbyAQSentinel +{ + public: + NearbyAQSentinel(Unit const* obj) : i_obj(obj) {} + bool operator()(Unit* u) + { + if (u->GetEntry() == 15264 && i_obj->IsWithinDistInMap(u, 70) && !u->isDead()) + return true; + else + return false; + } + private: + Unit const* i_obj; +}; + +struct MANGOS_DLL_DECL aqsentinelAI; +class MANGOS_DLL_DECL SentinelAbilityAura : public Aura +{ + public: + ~SentinelAbilityAura(); + Unit* GetTriggerTarget() const; + SentinelAbilityAura(aqsentinelAI *abilityOwner, SpellEntry *spell, uint32 ability, uint32 eff); + protected: + aqsentinelAI *aOwner; + int32 currentBasePoints; + uint32 abilityId; +}; + +struct MANGOS_DLL_DECL aqsentinelAI : public ScriptedAI +{ + uint32 ability; + int abselected; + + void selectAbility(int asel) + { + switch (asel) + { + case 0: ability = SPELL_MENDING_BUFF;break; + case 1: ability = SPELL_KNOCK_BUFF;break; + case 2: ability = SPELL_MANAB_BUFF;break; + case 3: ability = SPELL_REFLECTAF_BUFF;break; + case 4: ability = SPELL_REFLECTSFr_BUFF;break; + case 5: ability = SPELL_THORNS_BUFF;break; + case 6: ability = SPELL_THUNDER_BUFF;break; + case 7: ability = SPELL_MSTRIKE_BUFF;break; + case 8: ability = SPELL_STORM_BUFF;break; + } + } + + aqsentinelAI(Creature *c) : ScriptedAI(c) + { + ClearBudyList(); + abselected = 0; // just initialization of variable + Reset(); + } + + Creature *nearby[3]; + + void ClearBudyList() + { + nearby[0] = nearby[1] = nearby[2] = NULL; + } + + void AddBuddyToList(Creature *c) + { + if (c==m_creature) + return; + for (int i=0; i<3; i++) + { + if (nearby[i] == c) + return; + if (!nearby[i]) + { + nearby[i] = c; + return; + } + } + } + + void GiveBuddyMyList(Creature *c) + { + aqsentinelAI *cai = (aqsentinelAI *)(c->AI()); + for (int i=0; i<3; i++) + if (nearby[i] && nearby[i]!=c) + cai->AddBuddyToList(nearby[i]); + cai->AddBuddyToList(m_creature); + } + + void SendMyListToBuddies() + { + for (int i=0; i<3; i++) + if (nearby[i]) + GiveBuddyMyList(nearby[i]); + } + + void CallBuddiesToAttack(Unit *who) + { + for (int i=0; i<3; i++) + { + Creature *c = nearby[i]; + if (c) + { + if (!c->isInCombat()) + { + c->SetNoCallAssistence(true); + if(c->AI()) + c->AI()->AttackStart(who); + } + } + } + } + + void AddSentinelsNear(Unit *nears) + { + CellPair p(MaNGOS::ComputeCellPair(nears->GetPositionX(), nears->GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + std::list assistList; + + NearbyAQSentinel u_check(nears); + MaNGOS::CreatureListSearcher searcher(assistList, u_check); + TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_creature_searcher, *(nears->GetMap())); + + for(std::list::iterator iter = assistList.begin(); iter != assistList.end(); ++iter) + AddBuddyToList((*iter)); + } + + int pickAbilityRandom(bool *chosenAbilities) + { + for (int t = 0; t < 2; t++) + { + for (int i = !t ? (rand()%9) : 0; i < 9; i++) + { + if (!chosenAbilities[i]) + { + chosenAbilities[i] = true; + return i; + } + } + } + return 0; // should never happen + } + + void GetOtherSentinels(Unit *who) + { + bool *chosenAbilities = new bool[9]; + memset(chosenAbilities, 0, 9*sizeof(bool)); + selectAbility(pickAbilityRandom(chosenAbilities)); + + ClearBudyList(); + AddSentinelsNear(m_creature); + int bli; + for (bli = 0; bli < 3;bli++) + { + if (!nearby[bli]) + break; + AddSentinelsNear(nearby[bli]); + ((aqsentinelAI *)nearby[bli]->AI())->gatherOthersWhenAggro = false; + ((aqsentinelAI *)nearby[bli]->AI())->selectAbility(pickAbilityRandom(chosenAbilities)); + } + /*if (bli < 3) + DoYell("I dont have enough buddies.", LANG_NEUTRAL, 0);*/ + SendMyListToBuddies(); + CallBuddiesToAttack(who); + } + + bool gatherOthersWhenAggro; + + void Reset() + { + if (!m_creature->isDead()) + { + for (int i=0; i<3; i++) + { + if (!nearby[i]) + continue; + if (nearby[i]->isDead()) + nearby[i]->Respawn(); + } + } + ClearBudyList(); + gatherOthersWhenAggro = true; + m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false); + m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false); + } + + void GainSentinelAbility(uint32 id) + { + const SpellEntry *spell = GetSpellStore()->LookupEntry(id); + for (int i=0; i<3; i++) + { + if (!spell->Effect[i]) + continue; + SentinelAbilityAura *a = new SentinelAbilityAura(this, (SpellEntry *)spell, id, i); + m_creature->AddAura(a); + } + if (id == SPELL_KNOCK_BUFF) + { + m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); + m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); + } + } + + void Aggro(Unit *who) + { + if (gatherOthersWhenAggro) + GetOtherSentinels(who); + + GainSentinelAbility(ability); + DoZoneInCombat(); + } + + void JustDied(Unit*) + { + for (int ni=0; ni<3; ni++) + { + Creature *sent = nearby[ni]; + if (!sent) + continue; + if (sent->isDead()) + continue; + int h = sent->GetHealth() + (sent->GetMaxHealth() / 2); + if (h > sent->GetMaxHealth()) + h = sent->GetMaxHealth(); + sent->SetHealth(h); + ((aqsentinelAI *)sent->AI())->GainSentinelAbility(ability); + } + } + + Unit *GetHatedManaUser() + { + std::list::iterator i; + for (i = m_creature->getThreatManager().getThreatList().begin();i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid()); + if (pUnit->getPowerType()==POWER_MANA) + return pUnit; + } + return NULL; + } +}; +CreatureAI* GetAI_mob_anubisath_sentinelAI(Creature *_Creature) +{ + return new aqsentinelAI (_Creature); +} + +void AddSC_mob_anubisath_sentinel() +{ + Script *newscript; + newscript = new Script; + newscript->Name="mob_anubisath_sentinel"; + newscript->GetAI = GetAI_mob_anubisath_sentinelAI; + m_scripts[nrscripts++] = newscript; +} + +SentinelAbilityAura::~SentinelAbilityAura() {} +Unit* SentinelAbilityAura::GetTriggerTarget() const +{ + switch (abilityId) + { + case SPELL_KNOCK_BUFF: + case SPELL_THUNDER_BUFF: + case SPELL_MSTRIKE_BUFF: + case SPELL_STORM_BUFF: + return aOwner->m_creature->getVictim(); + + case SPELL_MANAB_BUFF: + return aOwner->GetHatedManaUser(); + + case SPELL_MENDING_BUFF: + case SPELL_REFLECTAF_BUFF: + case SPELL_REFLECTSFr_BUFF: + case SPELL_THORNS_BUFF: + default: + return aOwner->m_creature; + } +} + +SentinelAbilityAura::SentinelAbilityAura(aqsentinelAI *abilityOwner, SpellEntry *spell, uint32 ability, uint32 eff) +: Aura(spell, eff, NULL, abilityOwner->m_creature, abilityOwner->m_creature, NULL) +{ + aOwner = abilityOwner; + abilityId = ability; + currentBasePoints = 0; +} diff --git a/src/bindings/scripts/scripts/zone/terokkar_forest/terokkar_forest.cpp b/src/bindings/scripts/scripts/zone/terokkar_forest/terokkar_forest.cpp new file mode 100644 index 00000000000..2dc7e54297b --- /dev/null +++ b/src/bindings/scripts/scripts/zone/terokkar_forest/terokkar_forest.cpp @@ -0,0 +1,399 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Terokkar_Forest +SD%Complete: 80 +SDComment: Quest support: 9889(test script only, sql inside script), 10009, 10873, 10896, 11096. Skettis->Ogri'la Flight +SDCategory: Terokkar Forest +EndScriptData */ + +/* ContentData +mob_unkor_the_ruthless +mob_infested_root_walker +mob_rotting_forest_rager +mob_netherweb_victim +npc_floon +npc_skyguard_handler_deesak +EndContentData */ + +#include "precompiled.h" + +/*###### +## mob_unkor_the_ruthless +######*/ + +/* +UPDATE `creature_template` SET `ScriptName`='mob_unkor_the_ruthless' WHERE `entry`=18262; +*/ + +#define SAY_SUBMIT "I give up! Please don't kill me!" + +#define FACTION_HOSTILE 45 +#define FACTION_FRIENDLY 35 +#define QUEST_DONTKILLTHEFATONE 9889 + +#define SPELL_PULVERIZE 2676 +//#define SPELL_QUID9889 32174 + +struct MANGOS_DLL_DECL mob_unkor_the_ruthlessAI : public ScriptedAI +{ + mob_unkor_the_ruthlessAI(Creature* c) : ScriptedAI(c) { Reset(); } + + bool CanDoQuest; + uint32 UnkorUnfriendly_Timer; + uint32 Pulverize_Timer; + + void Reset() + { + CanDoQuest = false; + UnkorUnfriendly_Timer = 0; + Pulverize_Timer = 3000; + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, PLAYER_STATE_NONE); + m_creature->setFaction(FACTION_HOSTILE); + } + + void Aggro(Unit *who) {} + + void DoNice() + { + DoSay(SAY_SUBMIT,LANG_UNIVERSAL,NULL); + m_creature->setFaction(FACTION_FRIENDLY); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, PLAYER_STATE_SIT); + m_creature->RemoveAllAuras(); + m_creature->DeleteThreatList(); + m_creature->CombatStop(); + UnkorUnfriendly_Timer = 60000; + } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if( done_by->GetTypeId() == TYPEID_PLAYER ) + if( (m_creature->GetHealth()-damage)*100 / m_creature->GetMaxHealth() < 30 ) + { + if( Group* pGroup = ((Player*)done_by)->GetGroup() ) + { + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *pGroupie = itr->getSource(); + if( pGroupie && + pGroupie->GetQuestStatus(QUEST_DONTKILLTHEFATONE) == QUEST_STATUS_INCOMPLETE && + pGroupie->GetReqKillOrCastCurrentCount(QUEST_DONTKILLTHEFATONE, 18260) == 10 ) + { + pGroupie->AreaExploredOrEventHappens(QUEST_DONTKILLTHEFATONE); + if( !CanDoQuest ) + CanDoQuest = true; + } + } + } else + if( ((Player*)done_by)->GetQuestStatus(QUEST_DONTKILLTHEFATONE) == QUEST_STATUS_INCOMPLETE && + ((Player*)done_by)->GetReqKillOrCastCurrentCount(QUEST_DONTKILLTHEFATONE, 18260) == 10 ) + { + ((Player*)done_by)->AreaExploredOrEventHappens(QUEST_DONTKILLTHEFATONE); + CanDoQuest = true; + } + } + } + + void UpdateAI(const uint32 diff) + { + if( CanDoQuest ) + { + if( !UnkorUnfriendly_Timer ) + { + //DoCast(m_creature,SPELL_QUID9889); //not using spell for now + DoNice(); + } + else + { + if( UnkorUnfriendly_Timer < diff ) + { + EnterEvadeMode(); + }else UnkorUnfriendly_Timer -= diff; + } + } + + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( Pulverize_Timer < diff ) + { + DoCast(m_creature,SPELL_PULVERIZE); + Pulverize_Timer = 9000; + }else Pulverize_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_unkor_the_ruthless(Creature *_Creature) +{ + return new mob_unkor_the_ruthlessAI (_Creature); +} + +/*###### +## mob_infested_root_walker +######*/ + +struct MANGOS_DLL_DECL mob_infested_root_walkerAI : public ScriptedAI +{ + mob_infested_root_walkerAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() { } + void Aggro(Unit *who) { } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if (done_by && done_by->GetTypeId() == TYPEID_PLAYER) + if (m_creature->GetHealth() <= damage) + if (rand()%100 < 75) + //Summon Wood Mites + m_creature->CastSpell(m_creature,39130,true); + } +}; +CreatureAI* GetAI_mob_infested_root_walker(Creature *_Creature) +{ + return new mob_infested_root_walkerAI (_Creature); +} + +/*###### +## mob_rotting_forest_rager +######*/ + +struct MANGOS_DLL_DECL mob_rotting_forest_ragerAI : public ScriptedAI +{ + mob_rotting_forest_ragerAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() { } + void Aggro(Unit *who) { } + + void DamageTaken(Unit *done_by, uint32 &damage) + { + if (done_by->GetTypeId() == TYPEID_PLAYER) + if (m_creature->GetHealth() <= damage) + if (rand()%100 < 75) + //Summon Lots of Wood Mights + m_creature->CastSpell(m_creature,39134,true); + } +}; +CreatureAI* GetAI_mob_rotting_forest_rager(Creature *_Creature) +{ + return new mob_rotting_forest_ragerAI (_Creature); +} + +/*###### +## mob_netherweb_victim +######*/ + +#define QUEST_TARGET 22459 +//#define SPELL_FREE_WEBBED 38950 + +const uint32 netherwebVictims[6] = +{ + 18470, 16805, 21242, 18452, 22482, 21285 +}; +struct MANGOS_DLL_DECL mob_netherweb_victimAI : public ScriptedAI +{ + mob_netherweb_victimAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() { } + void Aggro(Unit *who) { } + void MoveInLineOfSight(Unit *who) { } + + void JustDied(Unit* Killer) + { + if( Killer->GetTypeId() == TYPEID_PLAYER ) + { + if( ((Player*)Killer)->GetQuestStatus(10873) == QUEST_STATUS_INCOMPLETE ) + { + if( rand()%100 < 25 ) + { + DoSpawnCreature(QUEST_TARGET,0,0,0,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,60000); + ((Player*)Killer)->KilledMonster(QUEST_TARGET, m_creature->GetGUID()); + }else + DoSpawnCreature(netherwebVictims[rand()%6],0,0,0,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,60000); + + if( rand()%100 < 75 ) + DoSpawnCreature(netherwebVictims[rand()%6],0,0,0,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,60000); + DoSpawnCreature(netherwebVictims[rand()%6],0,0,0,0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,60000); + } + } + } +}; +CreatureAI* GetAI_mob_netherweb_victim(Creature *_Creature) +{ + return new mob_netherweb_victimAI (_Creature); +} + +/*###### +## npc_floon +######*/ + +#define GOSSIP_FLOON1 "You owe Sim'salabim money. Hand them over or die!" +#define GOSSIP_FLOON2 "Hand over the money or die...again!" +#define SAY_FLOON_ATTACK "I choose the third option: KILLING YOU!" + +#define FACTION_HOSTILE_FL 1738 +#define FACTION_FRIENDLY_FL 35 + +#define SPELL_SILENCE 6726 +#define SPELL_FROSTBOLT 9672 +#define SPELL_FROST_NOVA 11831 + +struct MANGOS_DLL_DECL npc_floonAI : public ScriptedAI +{ + npc_floonAI(Creature* c) : ScriptedAI(c) { Reset(); } + + uint32 Silence_Timer; + uint32 Frostbolt_Timer; + uint32 FrostNova_Timer; + + void Reset() + { + Silence_Timer = 2000; + Frostbolt_Timer = 4000; + FrostNova_Timer = 9000; + m_creature->setFaction(FACTION_FRIENDLY_FL); + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( Silence_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_SILENCE); + Silence_Timer = 30000; + }else Silence_Timer -= diff; + + if( FrostNova_Timer < diff ) + { + DoCast(m_creature,SPELL_FROST_NOVA); + FrostNova_Timer = 20000; + }else FrostNova_Timer -= diff; + + if( Frostbolt_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_FROSTBOLT); + Frostbolt_Timer = 5000; + }else Frostbolt_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_floon(Creature *_Creature) +{ + return new npc_floonAI (_Creature); +} + +bool GossipHello_npc_floon(Player *player, Creature *_Creature ) +{ + if( player->GetQuestStatus(10009) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM(1, GOSSIP_FLOON1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(9442, _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_floon(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_ACTION_INFO_DEF ) + { + player->ADD_GOSSIP_ITEM(1, GOSSIP_FLOON2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(9443, _Creature->GetGUID()); + } + if( action == GOSSIP_ACTION_INFO_DEF+1 ) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->setFaction(FACTION_HOSTILE_FL); + ((npc_floonAI*)_Creature->AI())->DoSay(SAY_FLOON_ATTACK,LANG_UNIVERSAL,player); + ((npc_floonAI*)_Creature->AI())->AttackStart(player); + } + return true; +} + +/*###### +## npc_skyguard_handler_deesak +######*/ + +#define GOSSIP_SKYGUARD "Fly me to Ogri'la please" + +bool GossipHello_npc_skyguard_handler_deesak(Player *player, Creature *_Creature ) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetReputationRank(1031) >= REP_HONORED) + player->ADD_GOSSIP_ITEM( 2, GOSSIP_SKYGUARD, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_skyguard_handler_deesak(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + player->CLOSE_GOSSIP_MENU(); + player->CastSpell(player,41279,true); //TaxiPath 705 (Taxi - Skettis to Skyguard Outpost) + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_terokkar_forest() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mob_unkor_the_ruthless"; + newscript->GetAI = GetAI_mob_unkor_the_ruthless; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_infested_root_walker"; + newscript->GetAI = GetAI_mob_infested_root_walker; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_rotting_forest_rager"; + newscript->GetAI = GetAI_mob_rotting_forest_rager; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_netherweb_victim"; + newscript->GetAI = GetAI_mob_netherweb_victim; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_floon"; + newscript->pGossipHello = &GossipHello_npc_floon; + newscript->pGossipSelect = &GossipSelect_npc_floon; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_skyguard_handler_deesak"; + newscript->pGossipHello = &GossipHello_npc_skyguard_handler_deesak; + newscript->pGossipSelect = &GossipSelect_npc_skyguard_handler_deesak; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/thunder_bluff/thunder_bluff.cpp b/src/bindings/scripts/scripts/zone/thunder_bluff/thunder_bluff.cpp new file mode 100644 index 00000000000..b526325f5c9 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/thunder_bluff/thunder_bluff.cpp @@ -0,0 +1,136 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Thunder_Bluff +SD%Complete: 100 +SDComment: Quest support: 925 +SDCategory: Thunder Bluff +EndScriptData */ + +#include "precompiled.h" + +/*##### +# npc_cairne_bloodhoof +######*/ + +#define SPELL_BERSERKER_CHARGE 16636 +#define SPELL_CLEAVE 16044 +#define SPELL_MORTAL_STRIKE 16856 +#define SPELL_THUNDERCLAP 23931 +#define SPELL_UPPERCUT 22916 + +//TODO: verify abilities/timers +struct MANGOS_DLL_DECL npc_cairne_bloodhoofAI : public ScriptedAI +{ + npc_cairne_bloodhoofAI(Creature* c) : ScriptedAI(c) { Reset(); } + + uint32 BerserkerCharge_Timer; + uint32 Cleave_Timer; + uint32 MortalStrike_Timer; + uint32 Thunderclap_Timer; + uint32 Uppercut_Timer; + + void Reset() + { + BerserkerCharge_Timer = 30000; + Cleave_Timer = 5000; + MortalStrike_Timer = 10000; + Thunderclap_Timer = 15000; + Uppercut_Timer = 10000; + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( BerserkerCharge_Timer < diff ) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0); + if( target ) + DoCast(target,SPELL_BERSERKER_CHARGE); + BerserkerCharge_Timer = 25000; + }else BerserkerCharge_Timer -= diff; + + if( Uppercut_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_UPPERCUT); + Uppercut_Timer = 20000; + }else Uppercut_Timer -= diff; + + if( Thunderclap_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_THUNDERCLAP); + Thunderclap_Timer = 15000; + }else Thunderclap_Timer -= diff; + + if( MortalStrike_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_MORTAL_STRIKE); + MortalStrike_Timer = 15000; + }else MortalStrike_Timer -= diff; + + if( Cleave_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 7000; + }else Cleave_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_cairne_bloodhoof(Creature *_Creature) +{ + return new npc_cairne_bloodhoofAI (_Creature); +} + +bool GossipHello_npc_cairne_bloodhoof(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if( player->GetQuestStatus(925) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM( 0, "I know this is rather silly but a young ward who is a bit shy would like your hoofprint.", GOSSIP_SENDER_MAIN, GOSSIP_SENDER_INFO ); + + player->SEND_GOSSIP_MENU(7013, _Creature->GetGUID() ); + + return true; +} + +bool GossipSelect_npc_cairne_bloodhoof(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_SENDER_INFO ) + { + player->CastSpell(player, 23123, false); + player->SEND_GOSSIP_MENU(7014, _Creature->GetGUID() ); + } + return true; +} + +void AddSC_thunder_bluff() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_cairne_bloodhoof"; + newscript->GetAI = GetAI_npc_cairne_bloodhoof; + newscript->pGossipHello = &GossipHello_npc_cairne_bloodhoof; + newscript->pGossipSelect = &GossipSelect_npc_cairne_bloodhoof; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/tirisfal_glades/tirisfal_glades.cpp b/src/bindings/scripts/scripts/zone/tirisfal_glades/tirisfal_glades.cpp new file mode 100644 index 00000000000..8a02147eb41 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/tirisfal_glades/tirisfal_glades.cpp @@ -0,0 +1,88 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Tirisfal_Glades +SD%Complete: 100 +SDComment: Quest support: 590 +SDCategory: Tirisfal Glades +EndScriptData */ + +/* ContentData +npc_calvin_montague +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_calvin_montague +######*/ + +#define QUEST_590 590 +#define FACTION_FRIENDLY 68 +#define FACTION_HOSTILE 16 + +struct MANGOS_DLL_DECL npc_calvin_montagueAI : public ScriptedAI +{ + npc_calvin_montagueAI(Creature* c) : ScriptedAI(c) { Reset(); } + + void Reset() + { + m_creature->setFaction(FACTION_FRIENDLY); + } + + void Aggro(Unit* who) { } + + void JustDied(Unit* Killer) + { + if( Killer->GetTypeId() == TYPEID_PLAYER ) + if( ((Player*)Killer)->GetQuestStatus(QUEST_590) == QUEST_STATUS_INCOMPLETE ) + ((Player*)Killer)->AreaExploredOrEventHappens(QUEST_590); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_calvin_montague(Creature *_Creature) +{ + return new npc_calvin_montagueAI (_Creature); +} + +bool QuestAccept_npc_calvin_montague(Player* player, Creature* creature, Quest const* quest) +{ + if( quest->GetQuestId() == QUEST_590 ) + { + creature->setFaction(FACTION_HOSTILE); + ((npc_calvin_montagueAI*)creature->AI())->AttackStart(player); + } + return true; +} + +void AddSC_tirisfal_glades() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_calvin_montague"; + newscript->GetAI = GetAI_npc_calvin_montague; + newscript->pQuestAccept = &QuestAccept_npc_calvin_montague; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/uldaman/boss_ironaya.cpp b/src/bindings/scripts/scripts/zone/uldaman/boss_ironaya.cpp new file mode 100644 index 00000000000..57871381f9d --- /dev/null +++ b/src/bindings/scripts/scripts/zone/uldaman/boss_ironaya.cpp @@ -0,0 +1,107 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Ironaya +SD%Complete: 100 +SDComment: +SDCategory: Uldaman +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_ARCINGSMASH 39144 +#define SPELL_KNOCKAWAY 22893 +#define SPELL_WSTOMP 16727 + +#define SAY_AGGRO "None may steal the secrets of the makers!" +#define SOUND_AGGRO 5851 + +struct MANGOS_DLL_DECL boss_ironayaAI : public ScriptedAI +{ + boss_ironayaAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Arcing_Timer; + bool hasCastedWstomp; + bool hasCastedKnockaway; + + void Reset() + { + Arcing_Timer = 3000; + hasCastedKnockaway = false; + hasCastedWstomp = false; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //If we are <50% hp do knockaway ONCE + if (!hasCastedKnockaway && m_creature->GetHealth()*2 < m_creature->GetMaxHealth()) + { + m_creature->CastSpell(m_creature->getVictim(),SPELL_KNOCKAWAY, true); + + // current aggro target is knocked away pick new target + Unit* Target = SelectUnit(SELECT_TARGET_TOPAGGRO, 0); + + if (!Target || Target == m_creature->getVictim()) + Target = SelectUnit(SELECT_TARGET_TOPAGGRO, 1); + + if (Target) + m_creature->TauntApply(Target); + + //Shouldn't cast this agian + hasCastedKnockaway = true; + } + + //Arcing_Timer + if (Arcing_Timer < diff) + { + DoCast(m_creature,SPELL_ARCINGSMASH); + Arcing_Timer = 13000; + }else Arcing_Timer -= diff; + + if (!hasCastedWstomp && m_creature->GetHealth()*4 < m_creature->GetMaxHealth()) + { + DoCast(m_creature,SPELL_WSTOMP); + hasCastedWstomp = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_ironaya(Creature *_Creature) +{ + return new boss_ironayaAI (_Creature); +} + +void AddSC_boss_ironaya() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_ironaya"; + newscript->GetAI = GetAI_boss_ironaya; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/uldaman/uldaman.cpp b/src/bindings/scripts/scripts/zone/uldaman/uldaman.cpp new file mode 100644 index 00000000000..59bad6854fb --- /dev/null +++ b/src/bindings/scripts/scripts/zone/uldaman/uldaman.cpp @@ -0,0 +1,187 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Uldaman +SD%Complete: 100 +SDComment: Quest support: 2278 + 1 trash mob. +SDCategory: Uldaman +EndScriptData */ + +/* ContentData +mob_jadespine_basilisk +npc_lore_keeper_of_norgannon +EndContentData */ + +#include "precompiled.h" + +/*###### +## mob_jadespine_basilisk +######*/ + +#define SPELL_CSLUMBER 3636 + +struct MANGOS_DLL_DECL mob_jadespine_basiliskAI : public ScriptedAI +{ + mob_jadespine_basiliskAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Cslumber_Timer; + + void Reset() + { + Cslumber_Timer = 2000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Cslumber_Timer + if (Cslumber_Timer < diff) + { + //Cast + // DoCast(m_creature->getVictim(),SPELL_CSLUMBER); + m_creature->CastSpell(m_creature->getVictim(),SPELL_CSLUMBER, true); + + //Stop attacking target thast asleep and pick new target + Cslumber_Timer = 28000; + + Unit* Target = SelectUnit(SELECT_TARGET_TOPAGGRO, 0); + + if (!Target || Target == m_creature->getVictim()) + Target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + if (Target) + m_creature->TauntApply(Target); + + }else Cslumber_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_jadespine_basilisk(Creature *_Creature) +{ + return new mob_jadespine_basiliskAI (_Creature); +} + +/*###### +## npc_lore_keeper_of_norgannon +######*/ + +bool GossipHello_npc_lore_keeper_of_norgannon(Player *player, Creature *_Creature) +{ + if (player->GetQuestStatus(2278) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Who are the Earthen?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + + player->SEND_GOSSIP_MENU(1079, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_lore_keeper_of_norgannon(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "What is a \"subterranean being matrix\"?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(1080, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "What are the anomalies you speak of?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->SEND_GOSSIP_MENU(1081, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, "What is a resilient foundation of construction?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + player->SEND_GOSSIP_MENU(1082, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM( 0, "So... the Earthen were made out of stone?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(1083, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->ADD_GOSSIP_ITEM( 0, "Anything else I should know about the Earthen?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+6); + player->SEND_GOSSIP_MENU(1084, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+6: + player->ADD_GOSSIP_ITEM( 0, "I think I understand the Creators' design intent for the Earthen now. What are the Earthen's anomalies that you spoke of earlier?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+7); + player->SEND_GOSSIP_MENU(1085, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+7: + player->ADD_GOSSIP_ITEM( 0, "What high-stress environments would cause the Earthen to destabilize?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+8); + player->SEND_GOSSIP_MENU(1086, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+8: + player->ADD_GOSSIP_ITEM( 0, "What happens when the Earthen destabilize?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+9); + player->SEND_GOSSIP_MENU(1087, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+9: + player->ADD_GOSSIP_ITEM( 0, "Troggs?! Are the troggs you mention the same as the ones in the world today?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+10); + player->SEND_GOSSIP_MENU(1088, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+10: + player->ADD_GOSSIP_ITEM( 0, "You mentioned two results when the Earthen destabilize. What is the second?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+11); + player->SEND_GOSSIP_MENU(1089, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+11: + player->ADD_GOSSIP_ITEM( 0, "Dwarves!!! Now you're telling me that dwarves originally came from the Earthen?!", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+12); + player->SEND_GOSSIP_MENU(1090, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+12: + player->ADD_GOSSIP_ITEM( 0, "These dwarves are the same ones today, yes? Do the dwarves maintain any other links to the Earthen?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+13); + player->SEND_GOSSIP_MENU(1091, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+13: + player->ADD_GOSSIP_ITEM( 0, "Who are the Creators?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+14); + player->SEND_GOSSIP_MENU(1092, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+14: + player->ADD_GOSSIP_ITEM( 0, "This is a lot to think about.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+15); + player->SEND_GOSSIP_MENU(1093, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+15: + player->ADD_GOSSIP_ITEM( 0, "I will access the discs now.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+16); + player->SEND_GOSSIP_MENU(1094, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+16: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(2278); + break; + } + return true; +} + +void AddSC_uldaman() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="mob_jadespine_basilisk"; + newscript->GetAI = GetAI_mob_jadespine_basilisk; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_lore_keeper_of_norgannon"; + newscript->pGossipHello = &GossipHello_npc_lore_keeper_of_norgannon; + newscript->pGossipSelect = &GossipSelect_npc_lore_keeper_of_norgannon; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/undercity/undercity.cpp b/src/bindings/scripts/scripts/zone/undercity/undercity.cpp new file mode 100644 index 00000000000..b4845317612 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/undercity/undercity.cpp @@ -0,0 +1,260 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Undercity +SD%Complete: 95 +SDComment: Quest support: 6628(Parqual Fintallas questions/'answers' might have more to it, need more info), 9180(post-event). +SDCategory: Undercity +EndScriptData */ + +/* ContentData +npc_lady_sylvanas_windrunner +npc_highborne_lamenter +npc_parqual_fintallas +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_lady_sylvanas_windrunner +######*/ + +#define SAY_LAMENT_END "Belore..." +#define EMOTE_LAMENT_END "kneels down and pick up the amulet." + +#define SOUND_CREDIT 10896 +#define ENTRY_HIGHBORNE_LAMENTER 21628 +#define ENTRY_HIGHBORNE_BUNNY 21641 + +#define SPELL_HIGHBORNE_AURA 37090 +#define SPELL_SYLVANAS_CAST 36568 +#define SPELL_RIBBON_OF_SOULS 34432 //the real one to use might be 37099 + +float HighborneLoc[4][3]= +{ + {1285.41, 312.47, 0.51}, + {1286.96, 310.40, 1.00}, + {1289.66, 309.66, 1.52}, + {1292.51, 310.50, 1.99}, +}; +#define HIGHBORNE_LOC_Y -61.00 +#define HIGHBORNE_LOC_Y_NEW -55.50 + +struct MANGOS_DLL_DECL npc_lady_sylvanas_windrunnerAI : public ScriptedAI +{ + npc_lady_sylvanas_windrunnerAI(Creature *c) : ScriptedAI(c) { Reset(); } + + uint32 LamentEvent_Timer; + bool LamentEvent; + uint64 targetGUID; + + float myX; + float myY; + float myZ; + + void Reset() + { + myX = m_creature->GetPositionX(); + myY = m_creature->GetPositionY(); + myZ = m_creature->GetPositionZ(); + + LamentEvent_Timer = 5000; + LamentEvent = false; + targetGUID = 0; + } + + void Aggro(Unit *who) {} + + void JustSummoned(Creature *summoned) + { + if( summoned->GetEntry() == ENTRY_HIGHBORNE_BUNNY ) + { + if( Unit* target = Unit::GetUnit(*summoned,targetGUID) ) + { + target->SendMonsterMove(target->GetPositionX(),target->GetPositionY(),myZ+15.0,0,0,0); + target->Relocate(target->GetPositionX(),target->GetPositionY(),myZ+15.0); + summoned->CastSpell(target,SPELL_RIBBON_OF_SOULS,false); + } + + summoned->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT); + targetGUID = summoned->GetGUID(); + } + } + + void UpdateAI(const uint32 diff) + { + if( LamentEvent ) + { + if( LamentEvent_Timer < diff ) + { + float raX = myX; + float raY = myY; + float raZ = myZ; + + m_creature->GetRandomPoint(myX,myY,myZ,20.0,raX,raY,raZ); + m_creature->SummonCreature(ENTRY_HIGHBORNE_BUNNY,raX,raY,myZ,0,TEMPSUMMON_TIMED_DESPAWN,3000); + + LamentEvent_Timer = 2000; + if( !m_creature->HasAura(SPELL_SYLVANAS_CAST,0) ) + { + DoSay(SAY_LAMENT_END,LANG_UNIVERSAL,NULL); + DoTextEmote(EMOTE_LAMENT_END,NULL); + LamentEvent = false; + } + }else LamentEvent_Timer -= diff; + } + + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_lady_sylvanas_windrunner(Creature *_Creature) +{ + return new npc_lady_sylvanas_windrunnerAI (_Creature); +} + +bool ChooseReward_npc_lady_sylvanas_windrunner(Player *player, Creature *_Creature, const Quest *_Quest, uint32 slot) +{ + if( _Quest->GetQuestId() == 9180 ) + { + ((npc_lady_sylvanas_windrunnerAI*)_Creature->AI())->LamentEvent = true; + ((npc_lady_sylvanas_windrunnerAI*)_Creature->AI())->DoPlaySoundToSet(_Creature,SOUND_CREDIT); + _Creature->CastSpell(_Creature,SPELL_SYLVANAS_CAST,false); + + for( uint8 i = 0; i < 4; i++ ) + _Creature->SummonCreature(ENTRY_HIGHBORNE_LAMENTER, HighborneLoc[i][0], HighborneLoc[i][1], HIGHBORNE_LOC_Y, HighborneLoc[i][2], TEMPSUMMON_TIMED_DESPAWN, 160000); + } + + return true; +} + +/*###### +## npc_highborne_lamenter +######*/ + +struct MANGOS_DLL_DECL npc_highborne_lamenterAI : public ScriptedAI +{ + npc_highborne_lamenterAI(Creature *c) : ScriptedAI(c) { Reset(); } + + uint32 EventMove_Timer; + uint32 EventCast_Timer; + bool EventMove; + bool EventCast; + + void Reset() + { + EventMove_Timer = 10000; + EventCast_Timer = 17500; + EventMove = true; + EventCast = true; + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if( EventMove ) + { + if( EventMove_Timer < diff ) + { + m_creature->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT | MOVEMENTFLAG_LEVITATING); + m_creature->SendMonsterMoveWithSpeed(m_creature->GetPositionX(),m_creature->GetPositionY(),HIGHBORNE_LOC_Y_NEW,MOVEMENTFLAG_ONTRANSPORT,5000); + m_creature->GetMap()->CreatureRelocation(m_creature,m_creature->GetPositionX(),m_creature->GetPositionY(),HIGHBORNE_LOC_Y_NEW,m_creature->GetOrientation()); + EventMove = false; + }else EventMove_Timer -= diff; + } + if( EventCast ) + { + if( EventCast_Timer < diff ) + { + DoCast(m_creature,SPELL_HIGHBORNE_AURA); + EventCast = false; + }else EventCast_Timer -= diff; + } + } +}; +CreatureAI* GetAI_npc_highborne_lamenter(Creature *_Creature) +{ + return new npc_highborne_lamenterAI (_Creature); +} + +/*###### +## npc_parqual_fintallas +######*/ + +#define SPELL_MARK_OF_SHAME 6767 + +bool GossipHello_npc_parqual_fintallas(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(6628) == QUEST_STATUS_INCOMPLETE && !player->HasAura(SPELL_MARK_OF_SHAME,0) ) + { + player->ADD_GOSSIP_ITEM( 0, "Gul'dan", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->ADD_GOSSIP_ITEM( 0, "Kel'Thuzad", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->ADD_GOSSIP_ITEM( 0, "Ner'zhul", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->SEND_GOSSIP_MENU(5822, _Creature->GetGUID()); + } + else + player->SEND_GOSSIP_MENU(5821, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_parqual_fintallas(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->CastSpell(player,SPELL_MARK_OF_SHAME,false); + } + if (action == GOSSIP_ACTION_INFO_DEF+2) + { + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(6628); + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_undercity() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_lady_sylvanas_windrunner"; + newscript->GetAI = GetAI_npc_lady_sylvanas_windrunner; + newscript->pChooseReward = &ChooseReward_npc_lady_sylvanas_windrunner; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_highborne_lamenter"; + newscript->GetAI = GetAI_npc_highborne_lamenter; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_parqual_fintallas"; + newscript->pGossipHello = &GossipHello_npc_parqual_fintallas; + newscript->pGossipSelect = &GossipSelect_npc_parqual_fintallas; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/wailing_caverns/instance_wailing_caverns.cpp b/src/bindings/scripts/scripts/zone/wailing_caverns/instance_wailing_caverns.cpp new file mode 100644 index 00000000000..a67db38d4ed --- /dev/null +++ b/src/bindings/scripts/scripts/zone/wailing_caverns/instance_wailing_caverns.cpp @@ -0,0 +1,24 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Wailing_Caverns +SD%Complete: 0 +SDComment: Placeholder +SDCategory: Wailing Caverns +EndScriptData */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/scripts/zone/western_plaguelands/western_plaguelands.cpp b/src/bindings/scripts/scripts/zone/western_plaguelands/western_plaguelands.cpp new file mode 100644 index 00000000000..d6a20fb5cca --- /dev/null +++ b/src/bindings/scripts/scripts/zone/western_plaguelands/western_plaguelands.cpp @@ -0,0 +1,176 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Western_Plaguelands +SD%Complete: 90 +SDComment: Quest support: 5216,5219,5222,5225,5229,5231,5233,5235. To obtain Vitreous Focuser (could use more spesifics about gossip items) +SDCategory: Western Plaguelands +EndScriptData */ + +/* ContentData +npcs_dithers_and_arbington +npc_the_scourge_cauldron +EndContentData */ + +#include "precompiled.h" + +/*###### +## npcs_dithers_and_arbington +######*/ + +bool GossipHello_npcs_dithers_and_arbington(Player *player, Creature *_Creature) +{ + if(_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + if (_Creature->isVendor()) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + + if(player->GetQuestRewardStatus(5237) || player->GetQuestRewardStatus(5238)) + { + player->ADD_GOSSIP_ITEM(0, "What does the Felstone Field Cauldron need?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->ADD_GOSSIP_ITEM(0, "What does the Dalson's Tears Cauldron need?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->ADD_GOSSIP_ITEM(0, "What does the Writhing Haunt Cauldron need?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->ADD_GOSSIP_ITEM(0, "What does the Gahrron's Withering Cauldron need?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + player->SEND_GOSSIP_MENU(3985, _Creature->GetGUID()); + }else + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npcs_dithers_and_arbington(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch(action) + { + case GOSSIP_ACTION_TRADE: + player->SEND_VENDORLIST( _Creature->GetGUID() ); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM(0, "Thanks, i need a Vitreous Focuser", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(3980, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM(0, "Thanks, i need a Vitreous Focuser", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(3981, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM(0, "Thanks, i need a Vitreous Focuser", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(3982, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM(0, "Thanks, i need a Vitreous Focuser", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(3983, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->CLOSE_GOSSIP_MENU(); + _Creature->CastSpell(player, 17529, false); + break; + } + return true; +} + +/*###### +## npc_the_scourge_cauldron +######*/ + +struct MANGOS_DLL_DECL npc_the_scourge_cauldronAI : public ScriptedAI +{ + npc_the_scourge_cauldronAI(Creature *c) : ScriptedAI(c) {Reset();} + + void Reset() {} + + void Aggro(Unit* who) {} + + void DoDie() + { + //summoner dies here + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + //override any database `spawntimesecs` to prevent duplicated summons + uint32 rTime = m_creature->GetRespawnDelay(); + if( rTime<600 ) + m_creature->SetRespawnDelay(600); + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || who->GetTypeId() != TYPEID_PLAYER) + return; + + if(who->GetTypeId() == TYPEID_PLAYER) + { + switch(m_creature->GetAreaId()) + { + case 199: //felstone + if( ((Player*)who)->GetQuestStatus(5216) == QUEST_STATUS_INCOMPLETE || + ((Player*)who)->GetQuestStatus(5229) == QUEST_STATUS_INCOMPLETE ) + { + DoSpawnCreature(11075,0,0,0,m_creature->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); + DoDie(); + } + break; + case 200: //dalson + if( ((Player*)who)->GetQuestStatus(5219) == QUEST_STATUS_INCOMPLETE || + ((Player*)who)->GetQuestStatus(5231) == QUEST_STATUS_INCOMPLETE ) + { + DoSpawnCreature(11077,0,0,0,m_creature->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); + DoDie(); + } + break; + case 201: //gahrron + if( ((Player*)who)->GetQuestStatus(5225) == QUEST_STATUS_INCOMPLETE || + ((Player*)who)->GetQuestStatus(5235) == QUEST_STATUS_INCOMPLETE ) + { + DoSpawnCreature(11078,0,0,0,m_creature->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); + DoDie(); + } + break; + case 202: //writhing + if( ((Player*)who)->GetQuestStatus(5222) == QUEST_STATUS_INCOMPLETE || + ((Player*)who)->GetQuestStatus(5233) == QUEST_STATUS_INCOMPLETE ) + { + DoSpawnCreature(11076,0,0,0,m_creature->GetOrientation(),TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,600000); + DoDie(); + } + break; + } + } + } +}; +CreatureAI* GetAI_npc_the_scourge_cauldron(Creature *_Creature) +{ + return new npc_the_scourge_cauldronAI (_Creature); +} + +/*###### +## +######*/ + +void AddSC_western_plaguelands() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npcs_dithers_and_arbington"; + newscript->pGossipHello = &GossipHello_npcs_dithers_and_arbington; + newscript->pGossipSelect = &GossipSelect_npcs_dithers_and_arbington; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_the_scourge_cauldron"; + newscript->GetAI = GetAI_npc_the_scourge_cauldron; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/winterspring/winterspring.cpp b/src/bindings/scripts/scripts/zone/winterspring/winterspring.cpp new file mode 100644 index 00000000000..fa3ef8dd027 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/winterspring/winterspring.cpp @@ -0,0 +1,157 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Winterspring +SD%Complete: 90 +SDComment: Quest support: 5126 (Loraxs' tale missing proper gossip items text). Vendor Rivern Frostwind. Obtain Cache of Mau'ari +SDCategory: Winterspring +EndScriptData */ + +/* ContentData +npc_lorax +npc_rivern_frostwind +npc_witch_doctor_mauari +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_lorax +######*/ + +bool GossipHello_npc_lorax(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (player->GetQuestStatus(5126) == QUEST_STATUS_INCOMPLETE) + player->ADD_GOSSIP_ITEM( 0, "Talk to me", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_lorax(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, "What do you do here?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(3759, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+1: + player->ADD_GOSSIP_ITEM( 0, "I can help you", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(3760, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+2: + player->ADD_GOSSIP_ITEM( 0, "What deal?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->SEND_GOSSIP_MENU(3761, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+3: + player->ADD_GOSSIP_ITEM( 0, "Then what happened?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->SEND_GOSSIP_MENU(3762, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+4: + player->ADD_GOSSIP_ITEM( 0, "He is not safe, i'll make sure of that.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(3763, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF+5: + player->CLOSE_GOSSIP_MENU(); + player->AreaExploredOrEventHappens(5126); + break; + } + return true; +} + +/*###### +## npc_rivern_frostwind +######*/ + +bool GossipHello_npc_rivern_frostwind(Player *player, Creature *_Creature) +{ + if (_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if (_Creature->isVendor() && player->GetReputationRank(589) == REP_EXALTED) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_rivern_frostwind(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_TRADE) + player->SEND_VENDORLIST( _Creature->GetGUID() ); + + return true; +} + +/*###### +## npc_witch_doctor_mauari +######*/ + +bool GossipHello_npc_witch_doctor_mauari(Player *player, Creature *_Creature) +{ + if(_Creature->isQuestGiver()) + player->PrepareQuestMenu( _Creature->GetGUID() ); + + if(player->GetQuestRewardStatus(975)) + { + player->ADD_GOSSIP_ITEM(0, "I'd like you to make me a new Cache of Mau'ari please.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(3377, _Creature->GetGUID()); + }else + player->SEND_GOSSIP_MENU(3375, _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_witch_doctor_mauari(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action==GOSSIP_ACTION_INFO_DEF+1) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->CastSpell(player, 16351, false); + } + + return true; +} + +void AddSC_winterspring() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_lorax"; + newscript->pGossipHello = &GossipHello_npc_lorax; + newscript->pGossipSelect = &GossipSelect_npc_lorax; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_rivern_frostwind"; + newscript->pGossipHello = &GossipHello_npc_rivern_frostwind; + newscript->pGossipSelect = &GossipSelect_npc_rivern_frostwind; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_witch_doctor_mauari"; + newscript->pGossipHello = &GossipHello_npc_witch_doctor_mauari; + newscript->pGossipSelect = &GossipSelect_npc_witch_doctor_mauari; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zangarmarsh/zangarmarsh.cpp b/src/bindings/scripts/scripts/zone/zangarmarsh/zangarmarsh.cpp new file mode 100644 index 00000000000..e5917dd25ee --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zangarmarsh/zangarmarsh.cpp @@ -0,0 +1,283 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Zangarmarsh +SD%Complete: 100 +SDComment: Quest support: 9785, 9803, 10009. Mark Of ... buffs. +SDCategory: Zangarmarsh +EndScriptData */ + +/* ContentData +npcs_ashyen_and_keleth +npc_cooshcoosh +npc_elder_kuruti +npc_mortog_steamhead +EndContentData */ + +#include "precompiled.h" + +/*###### +## npcs_ashyen_and_keleth +######*/ + +#define GOSSIP_ITEM_BLESS_ASH "Grant me your mark, wise ancient." +#define GOSSIP_ITEM_BLESS_KEL "Grant me your mark, mighty ancient." +#define GOSSIP_REWARD_BLESS "You have my blessing" +//#define TEXT_BLESSINGS "" + +bool GossipHello_npcs_ashyen_and_keleth(Player *player, Creature *_Creature ) +{ + if (player->GetReputationRank(942) > REP_NEUTRAL) + { + if ( _Creature->GetEntry() == 17900) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_BLESS_ASH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + if ( _Creature->GetEntry() == 17901) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_BLESS_KEL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + } + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npcs_ashyen_and_keleth(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_INFO_DEF+1) + { + _Creature->setPowerType(POWER_MANA); + _Creature->SetMaxPower(POWER_MANA,200); //set a "fake" mana value, we can't depend on database doing it in this case + _Creature->SetPower(POWER_MANA,200); + + if ( _Creature->GetEntry() == 17900) //check which creature we are dealing with + { + switch (player->GetReputationRank(942)) + { //mark of lore + case REP_FRIENDLY: + _Creature->CastSpell(player, 31808, true); + _Creature->Say(GOSSIP_REWARD_BLESS, LANG_UNIVERSAL, 0); + break; + case REP_HONORED: + _Creature->CastSpell(player, 31810, true); + _Creature->Say(GOSSIP_REWARD_BLESS, LANG_UNIVERSAL, 0); + break; + case REP_REVERED: + _Creature->CastSpell(player, 31811, true); + _Creature->Say(GOSSIP_REWARD_BLESS, LANG_UNIVERSAL, 0); + break; + case REP_EXALTED: + _Creature->CastSpell(player, 31815, true); + _Creature->Say(GOSSIP_REWARD_BLESS, LANG_UNIVERSAL, 0); + break; + } + } + + if ( _Creature->GetEntry() == 17901) + { + switch (player->GetReputationRank(942)) //mark of war + { + case REP_FRIENDLY: + _Creature->CastSpell(player, 31807, true); + _Creature->Say(GOSSIP_REWARD_BLESS, LANG_UNIVERSAL, 0); + break; + case REP_HONORED: + _Creature->CastSpell(player, 31812, true); + _Creature->Say(GOSSIP_REWARD_BLESS, LANG_UNIVERSAL, 0); + break; + case REP_REVERED: + _Creature->CastSpell(player, 31813, true); + _Creature->Say(GOSSIP_REWARD_BLESS, LANG_UNIVERSAL, 0); + break; + case REP_EXALTED: + _Creature->CastSpell(player, 31814, true); + _Creature->Say(GOSSIP_REWARD_BLESS, LANG_UNIVERSAL, 0); + break; + } + } + player->CLOSE_GOSSIP_MENU(); + player->TalkedToCreature(_Creature->GetEntry(), _Creature->GetGUID()); + } + return true; +} + +/*###### +## npc_cooshcoosh +######*/ + +#define GOSSIP_COOSH "You owe Sim'salabim money. Hand them over or die!" + +#define FACTION_HOSTILE_CO 45 +#define FACTION_FRIENDLY_CO 35 + +#define SPELL_LIGHTNING_BOLT 9532 + +struct MANGOS_DLL_DECL npc_cooshcooshAI : public ScriptedAI +{ + npc_cooshcooshAI(Creature* c) : ScriptedAI(c) { Reset(); } + + uint32 LightningBolt_Timer; + + void Reset() + { + LightningBolt_Timer = 2000; + m_creature->setFaction(FACTION_FRIENDLY_CO); + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if(!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if( LightningBolt_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_LIGHTNING_BOLT); + LightningBolt_Timer = 5000; + }else LightningBolt_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_cooshcoosh(Creature *_Creature) +{ + return new npc_cooshcooshAI (_Creature); +} + +bool GossipHello_npc_cooshcoosh(Player *player, Creature *_Creature ) +{ + if( player->GetQuestStatus(10009) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM(1, GOSSIP_COOSH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(9441, _Creature->GetGUID()); + return true; +} + +bool GossipSelect_npc_cooshcoosh(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_ACTION_INFO_DEF ) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->setFaction(FACTION_HOSTILE_CO); + ((npc_cooshcooshAI*)_Creature->AI())->AttackStart(player); + } + return true; +} + +/*###### +## npc_elder_kuruti +######*/ + +#define GOSSIP_ITEM_KUR1 "Offer treat" +#define GOSSIP_ITEM_KUR2 "Im a messenger for Draenei" +#define GOSSIP_ITEM_KUR3 "Get message" + +bool GossipHello_npc_elder_kuruti(Player *player, Creature *_Creature ) +{ + if( player->GetQuestStatus(9803) == QUEST_STATUS_INCOMPLETE ) + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_KUR1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + + player->SEND_GOSSIP_MENU(9226,_Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_elder_kuruti(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + switch (action) + { + case GOSSIP_ACTION_INFO_DEF: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_KUR2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(9227, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 1: + player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM_KUR3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->SEND_GOSSIP_MENU(9229, _Creature->GetGUID()); + break; + case GOSSIP_ACTION_INFO_DEF + 2: + { + if( !player->HasItemCount(24573,1) ) + { + ItemPosCountVec dest; + uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, 24573, 1, false); + if( msg == EQUIP_ERR_OK ) + { + player->StoreNewItem( dest, 24573, true); + } + else + player->SendEquipError( msg,NULL,NULL ); + } + player->SEND_GOSSIP_MENU(9231, _Creature->GetGUID()); + break; + } + } + return true; +} + +/*###### +## npc_mortog_steamhead +######*/ + +bool GossipHello_npc_mortog_steamhead(Player *player, Creature *_Creature) +{ + if (_Creature->isVendor() && player->GetReputationRank(942) == REP_EXALTED) + player->ADD_GOSSIP_ITEM(1, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); + + player->SEND_GOSSIP_MENU(_Creature->GetNpcTextId(), _Creature->GetGUID()); + + return true; +} + +bool GossipSelect_npc_mortog_steamhead(Player *player, Creature *_Creature, uint32 sender, uint32 action) +{ + if (action == GOSSIP_ACTION_TRADE) + { + player->SEND_VENDORLIST( _Creature->GetGUID() ); + } + return true; +} + +/*###### +## AddSC +######*/ + +void AddSC_zangarmarsh() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npcs_ashyen_and_keleth"; + newscript->pGossipHello = &GossipHello_npcs_ashyen_and_keleth; + newscript->pGossipSelect = &GossipSelect_npcs_ashyen_and_keleth; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_cooshcoosh"; + newscript->pGossipHello = &GossipHello_npc_cooshcoosh; + newscript->pGossipSelect = &GossipSelect_npc_cooshcoosh; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_elder_kuruti"; + newscript->pGossipHello = &GossipHello_npc_elder_kuruti; + newscript->pGossipSelect = &GossipSelect_npc_elder_kuruti; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_mortog_steamhead"; + newscript->pGossipHello = &GossipHello_npc_mortog_steamhead; + newscript->pGossipSelect = &GossipSelect_npc_mortog_steamhead; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulaman/boss_janalai.cpp b/src/bindings/scripts/scripts/zone/zulaman/boss_janalai.cpp new file mode 100644 index 00000000000..0f3de44cd10 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulaman/boss_janalai.cpp @@ -0,0 +1,777 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Janalai +SD%Complete: 100 +SDComment: +SDCategory: Zul'Aman +EndScriptData */ + +#include "precompiled.h" +#include "def_zulaman.h" + +// Jan'alai +// --Spell +#define SPELL_FLAME_BREATH 43140 +#define SPELL_FIRE_WALL 43113 +#define SPELL_ENRAGE 44779 +#define SPELL_TELETOCENTER 43098 +#define SPELL_SUMMONALL 43097 +#define SPELL_BERSERK 47008 +// -- Fire Bob Spells +#define MOB_FIRE_BOMB 23920 +#define SPELL_FIRE_BOMB_CHANNEL 42621 +#define SPELL_FIRE_BOMB_THROW 42628 +#define SPELL_FIRE_BOMB_DUMMY 42629 +#define SPELL_FIRE_BOMB_DAMAGE 42630 + +// -- SAYs +#define SOUND_AGGRO 12031 +#define SAY_AGGRO "Spirits of da wind be your doom!" +#define SOUND_FIRE_BOMBS 12032 +#define SAY_FIRE_BOMBS "I burn ya now!" +#define SOUND_SUMMON_HATCHER 12033 +#define SAY_SUMMON_HATCHER "Where ma hatcha? Get to work on dem eggs!" +#define SOUND_ALL_EGGS 12034 +#define SAY_ALL_EGGS "I show you strength... in numbers." +#define SOUND_BERSERK 12035 +#define SAY_BERSERK "You done run outta time!" + +#define SOUND_SLAY_1 12036 +#define SAY_SLAY_1 "It all be over now, mon!" +#define SOUND_SLAY_2 12037 +#define SAY_SLAY_2 "Tazaga-choo!" + +#define SOUND_DEATH 12038 +#define SAY_DEATH "Zul'jin... got a surprise for you..." + +#define SOUND_AGGRO_1 12039 //NOT USED need more information + //NOT USED need more information (random say before aggro?) +#define SAY_AGGRO_1 "Come, strangers. The spirit of the dragonhawk hot be hungry for worthy souls." +#define SOUND_AGGRO_2 12040 //NOT USED need more information + //NOT USED need more information (random say before aggro?) +#define SAY_AGGRO_2 "Come, friends. Your bodies gonna feed ma hatchlings, and your souls are going to feed me with power!" + +// --Summons +#define MOB_AMANI_HATCHER 23818 +#define MOB_HATCHLING 23598 + +// -- Hatcher Spells +#define SPELL_HATCH_EGG 43734 + +// -- Hatchling Spells +#define SPELL_FLAMEBUFFED 43299 + +const int area_dx = 44; +const int area_dy = 51; + +float JanalainPos[1][3] = +{ + {-33.93, 1149.27, 19} +}; + +float FireWallCoords[4][4] = +{ + {-10.13, 1149.27, 19, 3.1415}, + {-33.93, 1123.90, 19, 0.5*3.1415}, + {-54.80, 1150.08, 19, 0}, + {-33.93, 1175.68, 19, 1.5*3.1415} +}; + +float hatcherway_l[5][3] = +{ + {-87.46,1170.09,6}, + {-74.41,1154.75,6}, + {-52.74,1153.32,19}, + {-33.37,1172.46,19}, + {-33.09,1203.87,19} +}; + +float hatcherway_r[5][3] = +{ + {-86.57,1132.85,6}, + {-73.94,1146.00,6}, + {-52.29,1146.51,19}, + {-33.57,1125.72,19}, + {-34.29,1095.22,19} +}; + +struct MANGOS_DLL_DECL boss_janalaiAI : public ScriptedAI +{ + boss_janalaiAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 fire_breath_timer; + uint32 bomb_timer; + uint32 throw_timer; + uint32 enrage_timer; + uint32 finishedbomb_timer; + uint32 bombcounter; + uint32 hatchertime; + uint32 eggs; + uint32 wipetimer; + uint32 reset_timer; + bool noeggs; + bool enraged; + bool enragetime; + + uint64 FireBombGUIDs[40]; + uint64 ThrowControllerGUID; + + bool bombing; + + void Reset() + { + if(pInstance) + pInstance->SetData(DATA_JANALAIEVENT, NOT_STARTED); + + fire_breath_timer = 8000; + bomb_timer = 30000; + enrage_timer = 300000; // 5 minutes + finishedbomb_timer = 6000; + throw_timer = 1000; + bombcounter = 0; + noeggs = false; + hatchertime = 10000; + wipetimer = 600000; // 10 mins + bombing =false; + reset_timer = 5000; + enraged = false; + enragetime = false; + + ThrowControllerGUID = 0; + + for(uint8 i = 0; i < 40; i++) + FireBombGUIDs[i] = 0; + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + + if(pInstance) + pInstance->SetData(DATA_JANALAIEVENT, DONE); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(SAY_SLAY_1, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_1); + break; + case 1: + DoYell(SAY_SLAY_2, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SLAY_1); + break; + } + } + + void Aggro(Unit *who) + { + if(pInstance) + pInstance->SetData(DATA_JANALAIEVENT, IN_PROGRESS); + + DoYell(SAY_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void FireWall() // Create Firewall + { + Creature* wall = NULL; + wall = m_creature->SummonCreature(MOB_FIRE_BOMB,FireWallCoords[0][0],FireWallCoords[0][1],FireWallCoords[0][2],FireWallCoords[0][3],TEMPSUMMON_TIMED_DESPAWN,11500); + if(wall) + wall->CastSpell(wall,SPELL_FIRE_WALL,false); + + wall = m_creature->SummonCreature(MOB_FIRE_BOMB,FireWallCoords[0][0],FireWallCoords[0][1]+5,FireWallCoords[0][2],FireWallCoords[0][3],TEMPSUMMON_TIMED_DESPAWN,11500); + if(wall) + wall->CastSpell(wall,SPELL_FIRE_WALL,false); + + wall = m_creature->SummonCreature(MOB_FIRE_BOMB,FireWallCoords[0][0],FireWallCoords[0][1]-5,FireWallCoords[0][2],FireWallCoords[0][3],TEMPSUMMON_TIMED_DESPAWN,11500); + if(wall) + wall->CastSpell(wall,SPELL_FIRE_WALL,false); + + wall = m_creature->SummonCreature(MOB_FIRE_BOMB,FireWallCoords[1][0]-2,FireWallCoords[1][1]-2,FireWallCoords[1][2],FireWallCoords[1][3],TEMPSUMMON_TIMED_DESPAWN,11500); + if(wall) + wall->CastSpell(wall,SPELL_FIRE_WALL,false); + + wall = m_creature->SummonCreature(MOB_FIRE_BOMB,FireWallCoords[1][0]+2,FireWallCoords[1][1]+2,FireWallCoords[1][2],FireWallCoords[1][3],TEMPSUMMON_TIMED_DESPAWN,11500); + if(wall) + wall->CastSpell(wall,SPELL_FIRE_WALL,false); + + wall = m_creature->SummonCreature(MOB_FIRE_BOMB,FireWallCoords[2][0],FireWallCoords[2][1],FireWallCoords[2][2],FireWallCoords[2][3],TEMPSUMMON_TIMED_DESPAWN,11500); + if(wall) + wall->CastSpell(wall,SPELL_FIRE_WALL,false); + + wall = m_creature->SummonCreature(MOB_FIRE_BOMB,FireWallCoords[2][0],FireWallCoords[2][1]-5,FireWallCoords[2][2],FireWallCoords[2][3],TEMPSUMMON_TIMED_DESPAWN,11500); + if(wall) + wall->CastSpell(wall,SPELL_FIRE_WALL,false); + + wall = m_creature->SummonCreature(MOB_FIRE_BOMB,FireWallCoords[2][0],FireWallCoords[2][1]+5,FireWallCoords[2][2],FireWallCoords[2][3],TEMPSUMMON_TIMED_DESPAWN,11500); + if(wall) + wall->CastSpell(wall,SPELL_FIRE_WALL,false); + + wall = m_creature->SummonCreature(MOB_FIRE_BOMB,FireWallCoords[3][0]-2,FireWallCoords[3][1],FireWallCoords[3][2],FireWallCoords[3][3],TEMPSUMMON_TIMED_DESPAWN,11500); + if(wall) + wall->CastSpell(wall,SPELL_FIRE_WALL,false); + + wall = m_creature->SummonCreature(MOB_FIRE_BOMB,FireWallCoords[3][0]+2,FireWallCoords[3][1],FireWallCoords[3][2],FireWallCoords[3][3],TEMPSUMMON_TIMED_DESPAWN,11500); + if(wall) + wall->CastSpell(wall,SPELL_FIRE_WALL,false); + } + + void throwBombs() // create Bombs + { + float dx; + float dy; + for ( int i(0); i < 40; i++) + { + dx = (rand()%(area_dx))-(area_dx/2); + dy = (rand()%(area_dy))-(area_dy/2); + + Creature* bomb = DoSpawnCreature(MOB_FIRE_BOMB, dx, dy, 0, 0, TEMPSUMMON_TIMED_DESPAWN, 13000); + if(bomb) + FireBombGUIDs[i] = bomb->GetGUID(); + } + + Creature* ThrowController = DoSpawnCreature(MOB_FIRE_BOMB,0,0,1,0,TEMPSUMMON_TIMED_DESPAWN,10000); + if(ThrowController) + ThrowControllerGUID = ThrowController->GetGUID(); + + bombcounter = 0; + } + + void throw5Bombs() //throwanimation + { + for ( int i(0); i < 5; i++) + { + Unit* ThrowController = NULL; + Unit* FireBomb = NULL; + if(ThrowControllerGUID) + ThrowController = Unit::GetUnit((*m_creature), ThrowControllerGUID); + + if(FireBombGUIDs[bombcounter]) + FireBomb = Unit::GetUnit((*m_creature), FireBombGUIDs[bombcounter]); + + if(ThrowController && FireBomb) + { + ThrowController->CastSpell(FireBomb,SPELL_FIRE_BOMB_THROW,true); + FireBomb->CastSpell(FireBomb,SPELL_FIRE_BOMB_DUMMY,false); + bombcounter ++; + } + } + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if(!bombing) // every Spell if NOT Bombing + { + //FIRE BREATH several videos says every 8Secounds + if(fire_breath_timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if(target) + DoCast(target,SPELL_FLAME_BREATH); + fire_breath_timer = 8000; + }else fire_breath_timer -=diff; + + if(bomb_timer < diff) + { + FireWall(); + bomb_timer = 20000+rand()%20000; + m_creature->Relocate(JanalainPos[0][0],JanalainPos[0][1],JanalainPos[0][2],0); + m_creature->SendMonsterMove(JanalainPos[0][0], JanalainPos[0][1],JanalainPos[0][2],0,0,100); + DoYell(SAY_FIRE_BOMBS, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_FIRE_BOMBS); + throwBombs(); + bombing = true; + + //Teleport every Player into the middle + Unit* Temp = NULL; + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for (; i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + Temp = Unit::GetUnit((*m_creature),(*i)->getUnitGuid()); + if (Temp && m_creature->GetDistance(Temp) > 30.0) + DoTeleportPlayer(Temp, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 0); + } + DoCast(m_creature,SPELL_TELETOCENTER,true); // only Effect Spell + DoCast(m_creature,SPELL_FIRE_BOMB_CHANNEL,false); + finishedbomb_timer = 11000; + }else bomb_timer -=diff; + + //enrage if under 25% hp before 5 min. + if (((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 25) && !enraged) + { + enragetime = true; + enrage_timer = 600000; + } + + //Enrage but only if not bombing + if(enragetime && !enraged) + { + DoYell(SAY_BERSERK, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_BERSERK); + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature,SPELL_ENRAGE); + enraged = true; + } + } + + //Enrage after 5 minutes + if(enrage_timer < diff) + { + enragetime = true; + enrage_timer = 600000; + }else enrage_timer -=diff; + + if(bombing) // every Spell if Bombing + { + if(bombcounter < 40) + { + if(throw_timer < diff) + { + throw5Bombs(); + throw_timer = 1000; + }else throw_timer -=diff; + } + + if(finishedbomb_timer < diff) + { + bombing = false; + finishedbomb_timer = 6000; + m_creature->RemoveAura(SPELL_FIRE_BOMB_CHANNEL,0); + m_creature->RemoveAura(SPELL_FIRE_BOMB_CHANNEL,1); + }else finishedbomb_timer -=diff; + } + + //Call Hatcher + if(!noeggs) + { + if(hatchertime < diff) + { + if(pInstance->GetData(DATA_J_EGGSLEFT)>0 || pInstance->GetData(DATA_J_EGGSRIGHT) > 0) + { + Unit* hatcher = NULL; + DoYell(SAY_SUMMON_HATCHER, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_SUMMON_HATCHER); + hatcher = m_creature->SummonCreature(MOB_AMANI_HATCHER,hatcherway_l[0][0],hatcherway_l[0][1],hatcherway_l[0][2],0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,10000); + if(hatcher) + hatcher->GetMotionMaster()->MovePoint(0, hatcherway_l[0][0], hatcherway_l[0][1], hatcherway_l[0][2]); + + hatcher = m_creature->SummonCreature(MOB_AMANI_HATCHER,hatcherway_r[0][0],hatcherway_r[0][1],hatcherway_r[0][2],0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,10000); + if(hatcher) + hatcher->GetMotionMaster()->MovePoint(0, hatcherway_r[0][0],hatcherway_r[0][1],hatcherway_r[0][2]); + hatchertime = 45000; + } + else + { + noeggs = true; + } + }else hatchertime -=diff; + } + + //WIPE after 10 minutes + if(wipetimer < diff) + { + DoYell(SAY_BERSERK, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_BERSERK); + DoCast(m_creature,SPELL_ENRAGE); + + wipetimer = 30000; + }else wipetimer -=diff; + + //Hatch All + if(!noeggs && (m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 35) + { + DoYell(SAY_ALL_EGGS , LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature,SOUND_BERSERK); + + if(pInstance) + eggs = pInstance->GetData(DATA_J_EGGSLEFT); + + int i; + for(i=1;i<=eggs;i=i+1) + { + int r = (rand()%20 - 10); + int s = (rand()%20 - 10); + m_creature->SummonCreature(MOB_HATCHLING,JanalainPos[0][0]+s,JanalainPos[0][1]+r,JanalainPos[0][2],0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,15000); + + if(pInstance) + pInstance->SetData(DATA_J_HATCHLEFT,1); + } + + if(pInstance) + eggs = pInstance->GetData(DATA_J_EGGSRIGHT); + + for(i=1;i<=eggs;i=i+1) + { + int r = (rand()%20 - 10); + int s = (rand()%20 - 10); + m_creature->SummonCreature(MOB_HATCHLING,JanalainPos[0][0]+s,JanalainPos[0][1]+r,JanalainPos[0][2],0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,15000); + if(pInstance) + pInstance->SetData(DATA_J_HATCHRIGHT,1); + } + + noeggs = true; + } + + //check for reset ... exploit preventing ... pulled from his podest + if(reset_timer < diff) + { + if(m_creature->GetPositionX() < -70 || m_creature->GetPositionX() > 0) + { + EnterEvadeMode(); + reset_timer = 5000; //every 5 Seca + } + }else reset_timer -=diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_janalaiAI(Creature *_Creature) +{ + return new boss_janalaiAI (_Creature); +} + +struct MANGOS_DLL_DECL mob_jandalai_firebombAI : public ScriptedAI +{ + mob_jandalai_firebombAI(Creature *c) : ScriptedAI(c){Reset();} + + uint32 bomb_timer; + + void Reset() + { + bomb_timer = 12000; + } + + void Aggro(Unit* who) {} + + void AttackStart(Unit* who) {} + + void MoveInLineOfSight(Unit* who) {} + + void UpdateAI(const uint32 diff) + { + if(bomb_timer < diff) //Boom + { + m_creature->CastSpell(m_creature,SPELL_FIRE_BOMB_DAMAGE,false); + bomb_timer = 1800000; + }else bomb_timer -=diff; + } +}; + +CreatureAI* GetAI_mob_jandalai_firebombAI(Creature *_Creature) +{ + return new mob_jandalai_firebombAI (_Creature); +} + +struct MANGOS_DLL_DECL mob_amanishi_hatcherAI : public ScriptedAI +{ + mob_amanishi_hatcherAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 waypoint; + bool waytype; + bool start; + bool hatch; + bool wait; + uint32 hatchlings; + uint32 waittimer; + uint32 eggs; + uint32 delete_timer; + + void Reset() + { + waypoint = 0; + waytype = 0; + hatch = false; + start = false; + wait = false; + waittimer = 4000; + hatchlings = 0; + eggs = 0; + delete_timer = 10000; + + //m_creature->RemoveAllAuras(); + //m_creature->DeleteThreatList(); + //m_creature->CombatStop(); + //DoGoHome(); + } + + void Aggro(Unit* who) + { + } + + void UpdateAI(const uint32 diff) + { + if(pInstance && (pInstance->GetData(DATA_JANALAIEVENT) == 1)) + { + if(!start && !hatch) + { + waytype = ( m_creature->GetPositionY() > 1150); + waypoint = 1; + start = true; + } + + if(start && !hatch) + { + if(wait) + { + if(waittimer < diff) + { + wait = false; + waittimer = 4000; + waypoint++; + + if (waypoint == 5) + { + hatch = true; + waittimer = 0; + hatchlings = 1; + } + } + else + { + waittimer -=diff; + } + } + else + { + m_creature->GetMotionMaster()->Clear(); + if(waytype) + m_creature->GetMotionMaster()->MovePoint(0,hatcherway_l[waypoint][0],hatcherway_l[waypoint][1],hatcherway_l[waypoint][2]); + else + m_creature->GetMotionMaster()->MovePoint(0,hatcherway_r[waypoint][0],hatcherway_r[waypoint][1],hatcherway_r[waypoint][2]); + wait= true; + } + } + + if(start && hatch) + { + if(waittimer < diff) + { + if(!pInstance) + return; + + waittimer = 4000; + Unit* hatchling; + + if(waytype) + { + eggs = pInstance->GetData(DATA_J_EGGSLEFT); + + if(eggs > 0) + DoCast(m_creature,SPELL_HATCH_EGG); + + int i; + for(i=1;i<=hatchlings;i=i+1) + { + eggs = pInstance->GetData(DATA_J_EGGSLEFT); + if(eggs <= 0) + { + if (waytype) waytype = false; else waytype = true; + waittimer = 15000; + hatch = false; + waypoint = 4; + wait = true; + i = hatchlings; + } + else + { + hatchling = DoSpawnCreature(MOB_HATCHLING,rand()%4-2,rand()%4-2,0,0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,15000); + pInstance->SetData(DATA_J_HATCHLEFT,1); + } + } + + DoCast(m_creature,SPELL_HATCH_EGG); + + if(hatchlings < 5) + hatchlings++; + } + else + { + eggs = pInstance->GetData(DATA_J_EGGSRIGHT); + + if(eggs > 0) + DoCast(m_creature,SPELL_HATCH_EGG); + + int i; + for(i=1;i<=hatchlings;i=i+1) + { + eggs = pInstance->GetData(DATA_J_EGGSRIGHT); + if(eggs <= 0) + { + if (waytype) waytype = false; else waytype = true; + waittimer = 15000; + hatch = false; + waypoint = 4; + wait = true; + i = hatchlings; + } + else + { + hatchling = DoSpawnCreature(MOB_HATCHLING,rand()%4-2,rand()%4-2,0,0,TEMPSUMMON_CORPSE_TIMED_DESPAWN,15000); + pInstance->SetData(DATA_J_HATCHRIGHT,1); + } + } + + DoCast(m_creature,SPELL_HATCH_EGG); + hatchlings++; + } + }else waittimer -=diff; + } + } + else + { + if(delete_timer < diff) + { + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + delete_timer = 10000; + }else delete_timer -=diff; + } + } +}; + +CreatureAI* GetAI_mob_amanishi_hatcherAI(Creature *_Creature) +{ + return new mob_amanishi_hatcherAI (_Creature); +} + +struct MANGOS_DLL_DECL mob_hatchlingAI : public ScriptedAI +{ + mob_hatchlingAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 buffer_timer; + uint32 delete_timer; + bool start; + + void Reset() + { + buffer_timer = 7000; + delete_timer = 10000; + start = false; + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + DoStartAttackAndMovement(who); + } + } + } + + void UpdateAI(const uint32 diff) + { + if(!start) + { + if(m_creature->GetPositionY() > 1150) + m_creature->GetMotionMaster()->MovePoint(0, hatcherway_l[3][0]+rand()%4-2,hatcherway_l[3][1]+rand()%4-2,hatcherway_l[3][2]); + else + m_creature->GetMotionMaster()->MovePoint(0,hatcherway_r[3][0]+rand()%4-2,hatcherway_r[3][1]+rand()%4-2,hatcherway_r[3][2]); + start = true; + } + + if(delete_timer < diff && (pInstance && !(pInstance->GetData(DATA_JANALAIEVENT) == 1))) + { + if(!(m_creature->getVictim())) + m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + delete_timer = 10000; + }else delete_timer -=diff; + + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + if (buffer_timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if(target) + DoCast(target,SPELL_FLAMEBUFFED); + + buffer_timer = 7000; + }else buffer_timer -=diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_mob_hatchlingAI(Creature *_Creature) +{ + return new mob_hatchlingAI (_Creature); +} + +void AddSC_boss_janalai() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_janalai"; + newscript->GetAI = GetAI_boss_janalaiAI; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_jandalai_firebomb"; + newscript->GetAI = GetAI_mob_jandalai_firebombAI; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_amanishi_hatcher"; + newscript->GetAI = GetAI_mob_amanishi_hatcherAI; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_hatchling"; + newscript->GetAI = GetAI_mob_hatchlingAI; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulaman/boss_nalorakk.cpp b/src/bindings/scripts/scripts/zone/zulaman/boss_nalorakk.cpp new file mode 100644 index 00000000000..01dae191217 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulaman/boss_nalorakk.cpp @@ -0,0 +1,272 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Nalorakk +SD%Complete: 80 +SDComment: Todo: Trash Waves +SDCategory: Zul'Aman +EndScriptData */ + +#include "precompiled.h" + +//TODO: Trash Waves + +//Unimplemented SoundIDs +/* +#define SOUND_NALORAKK_WAVE1 12066 +#define SOUND_NALORAKK_WAVE2 12067 +#define SOUND_NALORAKK_WAVE3 12068 +#define SOUND_NALORAKK_WAVE4 12069 + +#define SOUND_NALORAKK_EVENT1 12078 +#define SOUND_NALORAKK_EVENT2 12079 +*/ + +//General defines +#define YELL_AGGRO "You be dead soon enough!" +#define SOUND_YELL_AGGRO 12070 +#define YELL_KILL_ONE "Mua-ha-ha! Now whatchoo got to say?" +#define SOUND_YELL_KILL_ONE 12075 +#define YELL_KILL_TWO "Da Amani gonna rule again!" +#define SOUND_YELL_KILL_TWO 12076 +#define YELL_DEATH "I... be waitin' on da udda side...." +#define SOUND_YELL_DEATH 12077 + //Never seen this being used, so just guessing from what I hear. +#define YELL_BERSERK "You had your chance, now it be too late!" +#define SOUND_YELL_BERSERK 12074 + +#define SPELL_BERSERK 45078 //unsure, this increases damage, size and speed + +//Defines for Troll form +#define SPELL_BRUTALSWIPE 42384 +//#define SPELL_MANGLE 42389 This doesn't seem to apply the mangle debuff after all +#define SPELL_MANGLEEFFECT 44955 +#define SPELL_SURGE 42402 +#define SPELL_BEARFORM 42377 + +#define YELL_SURGE "I bring da pain!" +#define SOUND_YELL_SURGE 12071 + +#define YELL_SHIFTEDTOTROLL "Make way for Nalorakk!" +#define SOUND_YELL_TOTROLL 12073 + +//Defines for Bear form +#define SPELL_LACERATINGSLASH 42395 +#define SPELL_RENDFLESH 42397 +#define SPELL_DEAFENINGROAR 42398 + +#define YELL_SHIFTEDTOBEAR "You call on da beast, you gonna get more dan you bargain for!" +#define SOUND_YELL_TOBEAR 12072 + +struct MANGOS_DLL_DECL boss_nalorakkAI : public ScriptedAI +{ + boss_nalorakkAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ChangeForm_Timer; + uint32 BrutalSwipe_Timer; + uint32 Mangle_Timer; + uint32 Surge_Timer; + uint32 LaceratingSlash_Timer; + uint32 RendFlesh_Timer; + uint32 DeafeningRoar_Timer; + uint32 ShapeShiftCheck_Timer; + uint32 Berserk_Timer; + bool inBearForm; + bool Berserking; + bool ChangedToBear; + bool ChangedToTroll; + + void Reset() + { + ChangeForm_Timer = 45000; + BrutalSwipe_Timer = 12000; + Mangle_Timer = 15000; + Surge_Timer = 20000; + LaceratingSlash_Timer = 6000; + RendFlesh_Timer = 6000; + DeafeningRoar_Timer = 20000; + ShapeShiftCheck_Timer = 40000; + Berserk_Timer = 600000; + inBearForm = false; + Berserking = false; + ChangedToBear = false; + ChangedToTroll = true; + } + + void Aggro(Unit *who) + { + DoYell(YELL_AGGRO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_YELL_AGGRO); + } + + void KilledUnit(Unit* victim) + { + switch(rand()%2) + { + case 0: + DoYell(YELL_KILL_ONE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_YELL_KILL_ONE); + break; + case 1: + DoYell(YELL_KILL_TWO, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_YELL_KILL_TWO); + break; + } + } + + void JustDied(Unit* Killer) + { + DoYell(YELL_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature, SOUND_YELL_DEATH); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Berserking + if ((Berserk_Timer < diff) && (!Berserking)) + { + DoCast(m_creature, SPELL_BERSERK); + DoYell(YELL_BERSERK, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_YELL_BERSERK); + Berserking = true; + }else Berserk_Timer -= diff; + + //Don't check if we're shapeshifted every UpdateAI + if (ShapeShiftCheck_Timer < diff) + { + //This will return true if we have bearform aura + inBearForm = m_creature->HasAura(SPELL_BEARFORM, 0); + ShapeShiftCheck_Timer = 1000; + }else ShapeShiftCheck_Timer -= diff; + + //Spells for Troll Form (only to be casted if we NOT have bear phase aura) + if (!inBearForm) + { + //We just changed to troll form! + if (!ChangedToTroll) + { + DoYell(YELL_SHIFTEDTOTROLL, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_YELL_TOTROLL); + ChangedToTroll = true; + ChangedToBear = false; + //Reset spell timers + LaceratingSlash_Timer = 6000 + rand()%19000; + RendFlesh_Timer = 6000 + rand()%19000; + DeafeningRoar_Timer = 15000 + rand()%10000; + ShapeShiftCheck_Timer = 40000; + } + + //Brutal Swipe (some sources may say otherwise, but I've never seen this in Bear form) + if (BrutalSwipe_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_BRUTALSWIPE); + BrutalSwipe_Timer = 7000 + rand()%13000; + }else BrutalSwipe_Timer -= diff; + + //Mangle + if (Mangle_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_MANGLEEFFECT); + Mangle_Timer = 3000 + rand()%17000; + }else Mangle_Timer -= diff; + + //Surge + if (Surge_Timer < diff) + { + //select a random unit other than the main tank + Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 1); + + //if there aren't other units, cast on the tank + if(!target) + target = m_creature->getVictim(); + + DoCast(target, SPELL_SURGE); + + DoYell(YELL_SURGE, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_YELL_SURGE); + + Surge_Timer = 15000 + rand()%17500; + }else Surge_Timer -= diff; + + //Change to Bear Form if we're in Troll Form for 45sec + if (ChangeForm_Timer < diff) + { + DoCast(m_creature, SPELL_BEARFORM); + //And 30sec (bear form) + 45sec (troll form) before we should cast this again + ChangeForm_Timer = 75000; + }else ChangeForm_Timer -= diff; + } + //Spells for Bear Form (only to be casted if we have bear phase aura) + else + { + //We just changed to bear form! + if (!ChangedToBear) + { + DoYell(YELL_SHIFTEDTOBEAR, LANG_UNIVERSAL, NULL); + DoPlaySoundToSet(m_creature, SOUND_YELL_TOBEAR); + ChangedToBear = true; + ChangedToTroll = false; + //Reset spell timers + Surge_Timer = 15000 + rand()%17500; + BrutalSwipe_Timer = 7000 + rand()%13000; + Mangle_Timer = 3000 + rand()%17000; + ShapeShiftCheck_Timer = 25000; + } + + //Lacerating Slash + if (LaceratingSlash_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_LACERATINGSLASH); + LaceratingSlash_Timer = 6000 + rand()%19000; + }else LaceratingSlash_Timer -= diff; + + //Rend Flesh + if (RendFlesh_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_RENDFLESH); + RendFlesh_Timer = 6000 + rand()%19000; + }else RendFlesh_Timer -= diff; + + //Deafening Roar + if (DeafeningRoar_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_DEAFENINGROAR); + DeafeningRoar_Timer = 15000 + rand()%10000; + }else DeafeningRoar_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_nalorakk(Creature *_Creature) +{ + return new boss_nalorakkAI (_Creature); +} + +void AddSC_boss_nalorakk() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_nalorakk"; + newscript->GetAI = GetAI_boss_nalorakk; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulaman/def_zulaman.h b/src/bindings/scripts/scripts/zone/zulaman/def_zulaman.h new file mode 100644 index 00000000000..7a57eba2f51 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulaman/def_zulaman.h @@ -0,0 +1,17 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ZULAMAN_H +#define DEF_ZULAMAN_H + +#define DATA_JANALAI 1 +#define DATA_JANALAIEVENT 2 +#define DATA_J_EGGSLEFT 3 +#define DATA_J_EGGSRIGHT 4 +#define DATA_J_HATCHLEFT 5 +#define DATA_J_HATCHRIGHT 6 + +#define TYPE_RAND_VENDOR_1 7 +#define TYPE_RAND_VENDOR_2 8 +#endif diff --git a/src/bindings/scripts/scripts/zone/zulaman/instance_zulaman.cpp b/src/bindings/scripts/scripts/zone/zulaman/instance_zulaman.cpp new file mode 100644 index 00000000000..7283a27d3a8 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulaman/instance_zulaman.cpp @@ -0,0 +1,137 @@ +/* Copyright (C) 2006,2007 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_Zulaman +SD%Complete: 80 +SDComment: +SDCategory: Zul'Aman +EndScriptData */ + +#include "precompiled.h" +#include "def_zulaman.h" + +#define ENCOUNTERS 1 +#define RAND_VENDOR 2 + +struct MANGOS_DLL_DECL instance_zulaman : public ScriptedInstance +{ + instance_zulaman(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + uint64 janalai; + uint32 janalai_eggs_l; + uint32 janalai_eggs_r; + + uint32 Encounters[ENCOUNTERS]; + uint32 RandVendor[RAND_VENDOR]; + + void Initialize() + { + janalai = 0; + janalai_eggs_l = 20; + janalai_eggs_r = 20; + + for(uint8 i = 0; i < ENCOUNTERS; i++) + Encounters[i] = NOT_STARTED; + for(uint8 i = 0; i < RAND_VENDOR; i++) + RandVendor[i] = NOT_STARTED; + } + + bool IsEncounterInProgress() const + { + for(uint8 i = 0; i < ENCOUNTERS; i++) + if(Encounters[i] == IN_PROGRESS) return true; + + return false; + } + + void OnCreatureCreate(Creature *creature, uint32 creature_entry) + { + switch(creature_entry) + { + case 23578: + janalai = creature->GetGUID(); + break; + } + } + + uint64 GetData64(uint32 identifier) + { + if(identifier == DATA_JANALAI && janalai) + return janalai; + + return 0; + } + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_JANALAIEVENT: + if(data == 0) + { + janalai_eggs_l = 20; + janalai_eggs_r = 20; + } + Encounters[0] = data; + break; + case DATA_J_HATCHLEFT: + janalai_eggs_l -= data; + break; + case DATA_J_HATCHRIGHT: + janalai_eggs_r -= data; + break; + case TYPE_RAND_VENDOR_1: + RandVendor[0] = data; + break; + case TYPE_RAND_VENDOR_2: + RandVendor[1] = data; + break; + } + } + + uint32 GetData(uint32 type) + { + switch(type) + { + case DATA_JANALAIEVENT: + return Encounters[0]; + case DATA_J_EGGSLEFT: + return janalai_eggs_l; + case DATA_J_EGGSRIGHT: + return janalai_eggs_r; + case TYPE_RAND_VENDOR_1: + return RandVendor[0]; + case TYPE_RAND_VENDOR_2: + return RandVendor[1]; + } + return 0; + } +}; + +InstanceData* GetInstanceData_instance_zulaman(Map* map) +{ + return new instance_zulaman(map); +} + +void AddSC_instance_zulaman() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_zulaman"; + newscript->GetInstanceData = GetInstanceData_instance_zulaman; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulaman/zulaman.cpp b/src/bindings/scripts/scripts/zone/zulaman/zulaman.cpp new file mode 100644 index 00000000000..965fc6cb11b --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulaman/zulaman.cpp @@ -0,0 +1,108 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Zulaman +SD%Complete: 90 +SDComment: Forest Frog will turn into different NPC's. Workaround to prevent new entry from running this script +SDCategory: Zul'Aman +EndScriptData */ + +/* ContentData +npc_forest_frog +EndContentData */ + +#include "precompiled.h" +#include "def_zulaman.h" + +/*###### +## npc_forest_frog +######*/ + +#define SPELL_REMOVE_AMANI_CURSE 43732 +#define SPELL_PUSH_MOJO 43923 +#define ENTRY_FOREST_FROG 24396 + +struct MANGOS_DLL_DECL npc_forest_frogAI : public ScriptedAI +{ + npc_forest_frogAI(Creature* c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + void Reset() { } + + void Aggro(Unit *who) { } + + void DoSpawnRandom() + { + if( pInstance ) + { + uint32 cEntry = 0; + switch(rand()%11) + { + case 0: cEntry = 24024; break; //Kraz + case 1: cEntry = 24397; break; //Mannuth + case 2: cEntry = 24403; break; //Deez + case 3: cEntry = 24404; break; //Galathryn + case 4: cEntry = 24405; break; //Adarrah + case 5: cEntry = 24406; break; //Fudgerick + case 6: cEntry = 24407; break; //Darwen + case 7: cEntry = 24445; break; //Mitzi + case 8: cEntry = 24448; break; //Christian + case 9: cEntry = 24453; break; //Brennan + case 10: cEntry = 24455; break; //Hollee + } + + if( !pInstance->GetData(TYPE_RAND_VENDOR_1) ) + if(rand()%10 == 1) cEntry = 24408; //Gunter + if( !pInstance->GetData(TYPE_RAND_VENDOR_2) ) + if(rand()%10 == 1) cEntry = 24409; //Kyren + + if( cEntry ) m_creature->UpdateEntry(cEntry); + + if( cEntry == 24408) pInstance->SetData(TYPE_RAND_VENDOR_1,DONE); + if( cEntry == 24409) pInstance->SetData(TYPE_RAND_VENDOR_2,DONE); + } + } + + void SpellHit(Unit *caster, const SpellEntry *spell) + { + if( spell->Id == SPELL_REMOVE_AMANI_CURSE && caster->GetTypeId() == TYPEID_PLAYER && m_creature->GetEntry() == ENTRY_FOREST_FROG ) + { + //increase or decrease chance of mojo? + if( rand()%99 == 50 ) DoCast(caster,SPELL_PUSH_MOJO,true); + else DoSpawnRandom(); + } + } +}; +CreatureAI* GetAI_npc_forest_frog(Creature *_Creature) +{ + return new npc_forest_frogAI (_Creature); +} + +void AddSC_zulaman() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_forest_frog"; + newscript->GetAI = GetAI_npc_forest_frog; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulfarrak/zulfarrak.cpp b/src/bindings/scripts/scripts/zone/zulfarrak/zulfarrak.cpp new file mode 100644 index 00000000000..c526a83ad7e --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulfarrak/zulfarrak.cpp @@ -0,0 +1,224 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Zulfarrak +SD%Complete: 50 +SDComment: Consider it temporary, no instance script made for this instance yet. +SDCategory: Zul'Farrak +EndScriptData */ + +/* ContentData +npc_sergeant_bly +npc_weegli_blastfuse +EndContentData */ + +#include "precompiled.h" + +/*###### +## npc_sergeant_bly +######*/ + +#define FACTION_HOSTILE 14 +#define FACTION_FRIENDLY 35 + +#define SPELL_SHIELD_BASH 11972 +#define SPELL_REVENGE 12170 + +#define GOSSIP_BLY "[PH] In that case, i will take my reward!" + +struct MANGOS_DLL_DECL npc_sergeant_blyAI : public ScriptedAI +{ + npc_sergeant_blyAI(Creature *c) : ScriptedAI(c) + { + //pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + //ScriptedInstance* pInstance; + + uint32 ShieldBash_Timer; + uint32 Revenge_Timer; //this is wrong, spell should never be used unless m_creature->getVictim() dodge, parry or block attack. Mangos support required. + + void Reset() + { + ShieldBash_Timer = 5000; + Revenge_Timer = 8000; + + m_creature->setFaction(FACTION_FRIENDLY); + + /*if( pInstance ) + pInstance->SetData(0, NOT_STARTED);*/ + } + + void Aggro(Unit *who) + { + /*if( pInstance ) + pInstance->SetData(0, IN_PROGRESS);*/ + } + + void JustDied(Unit *victim) + { + /*if( pInstance ) + pInstance->SetData(0, DONE);*/ + } + + void UpdateAI(const uint32 diff) + { + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + if( ShieldBash_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_SHIELD_BASH); + ShieldBash_Timer = 15000; + }else ShieldBash_Timer -= diff; + + if( Revenge_Timer < diff ) + { + DoCast(m_creature->getVictim(),SPELL_REVENGE); + Revenge_Timer = 10000; + }else Revenge_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_sergeant_bly(Creature *_Creature) +{ + return new npc_sergeant_blyAI (_Creature); +} + +bool GossipHello_npc_sergeant_bly(Player *player, Creature *_Creature ) +{ + /*if( pInstance->GetData(0) == DONE ) + {*/ + player->ADD_GOSSIP_ITEM(1, GOSSIP_BLY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(1517, _Creature->GetGUID()); + /*} + else if( pInstance->GetData(0) == IN_PROGRESS ) + player->SEND_GOSSIP_MENU(1516, _Creature->GetGUID()); + else + player->SEND_GOSSIP_MENU(1515, _Creature->GetGUID());*/ + + return true; +} + +bool GossipSelect_npc_sergeant_bly(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_ACTION_INFO_DEF+1 ) + { + player->CLOSE_GOSSIP_MENU(); + _Creature->setFaction(FACTION_HOSTILE); + ((npc_sergeant_blyAI*)_Creature->AI())->AttackStart(player); + } + return true; +} + +/*###### +## npc_weegli_blastfuse +######*/ + +#define SPELL_BOMB 8858 +#define SPELL_GOBLIN_LAND_MINE 21688 +#define SPELL_SHOOT 6660 +#define SPELL_WEEGLIS_BARREL 10772 + +#define GOSSIP_WEEGLI "[PH] Please blow up the door." + +struct MANGOS_DLL_DECL npc_weegli_blastfuseAI : public ScriptedAI +{ + npc_weegli_blastfuseAI(Creature *c) : ScriptedAI(c) + { + //pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + //ScriptedInstance* pInstance; + + void Reset() + { + /*if( pInstance ) + pInstance->SetData(0, NOT_STARTED);*/ + } + + void Aggro(Unit *who) + { + /*if( pInstance ) + pInstance->SetData(0, IN_PROGRESS);*/ + } + + void JustDied(Unit *victim) + { + /*if( pInstance ) + pInstance->SetData(0, DONE);*/ + } + + void UpdateAI(const uint32 diff) + { + if( !m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_npc_weegli_blastfuse(Creature *_Creature) +{ + return new npc_weegli_blastfuseAI (_Creature); +} + +bool GossipHello_npc_weegli_blastfuse(Player *player, Creature *_Creature ) +{ + //event not implemented yet, this is only placeholder for future developement + /*if( pInstance->GetData(0) == DONE ) + { + player->ADD_GOSSIP_ITEM(1, GOSSIP_WEEGLI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->SEND_GOSSIP_MENU(1514, _Creature->GetGUID());//if event can proceed to end + } + else if( pInstance->GetData(0) == IN_PROGRESS ) + player->SEND_GOSSIP_MENU(1513, _Creature->GetGUID());//if event are in progress + else*/ + player->SEND_GOSSIP_MENU(1511, _Creature->GetGUID()); //if event not started + return true; +} + +bool GossipSelect_npc_weegli_blastfuse(Player *player, Creature *_Creature, uint32 sender, uint32 action ) +{ + if( action == GOSSIP_ACTION_INFO_DEF+1 ) + { + player->CLOSE_GOSSIP_MENU(); + //here we make him run to door, set the charge and run away off to nowhere + } + return true; +} + +void AddSC_zulfarrak() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="npc_sergeant_bly"; + newscript->GetAI = GetAI_npc_sergeant_bly; + newscript->pGossipHello = &GossipHello_npc_sergeant_bly; + newscript->pGossipSelect = &GossipSelect_npc_sergeant_bly; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="npc_weegli_blastfuse"; + newscript->GetAI = GetAI_npc_weegli_blastfuse; + newscript->pGossipHello = &GossipHello_npc_weegli_blastfuse; + newscript->pGossipSelect = &GossipSelect_npc_weegli_blastfuse; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_arlokk.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_arlokk.cpp new file mode 100644 index 00000000000..7be65ad73f0 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_arlokk.cpp @@ -0,0 +1,211 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Arlokk +SD%Complete: 95 +SDComment: Wrong cleave and red aura is missing. +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_SHADOWWORDPAIN 23952 +#define SPELL_GOUGE 24698 +#define SPELL_MARK 24210 +#define SPELL_CLEAVE 26350 //Perhaps not right. Not a red aura... +#define SPELL_PANTHER_TRANSFORM 24190 + +#define SAY_TRANSFORM "Bethekk, your priestess calls upon your might!" +#define SAY_DEATH "At last I am free of the soul flayer..." + +#define SOUND_TRANSFORM 8416 +#define SOUND_DEATH 8412 + +struct MANGOS_DLL_DECL boss_arlokkAI : public ScriptedAI +{ + boss_arlokkAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ShadowWordPain_Timer; + uint32 Gouge_Timer; + uint32 Mark_Timer; + uint32 Cleave_Timer; + uint32 Vanish_Timer; + uint32 Summon_Timer; + uint32 Visible_Timer; + + Unit* markedTarget; + Creature *Panther; + uint32 Counter; + + bool PhaseTwo; + bool VanishedOnce; + + void Reset() + { + ShadowWordPain_Timer = 8000; + Gouge_Timer = 14000; + Mark_Timer = 35000; + Cleave_Timer = 4000; + Vanish_Timer = 60000; + Summon_Timer = 5000; + Visible_Timer = 6000; + + Counter = 0; + + markedTarget = NULL; + PhaseTwo = false; + VanishedOnce = false; + + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,15218); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit *who) + { + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,15218); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + + if(pInstance) + pInstance->SetData(DATA_ARLOKK_DEATH, 0); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget()) + return; + + if( m_creature->getVictim() && m_creature->isAlive()) + { + if (!PhaseTwo && ShadowWordPain_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SHADOWWORDPAIN); + ShadowWordPain_Timer = 15000; + }else ShadowWordPain_Timer -= diff; + + if (!PhaseTwo && Mark_Timer < diff) + { + markedTarget = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(markedTarget,SPELL_MARK); + Mark_Timer = 15000; + }else Mark_Timer -= diff; + + if (Summon_Timer < diff && Counter < 31) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + Panther = m_creature->SummonCreature(15101,-11532.79980,-1649.6734,41.4800,0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + + if(markedTarget && Panther ) + { Panther ->AI()->AttackStart(markedTarget); } + else + { Panther ->AI()->AttackStart(target); } + + Panther = m_creature->SummonCreature(15101,-11532.9970,-1606.4840,41.2979,0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + + if(markedTarget && Panther ) + { Panther ->AI()->AttackStart(markedTarget); } + else + { Panther ->AI()->AttackStart(target); } + + Counter++; + Summon_Timer = 5000; + }else Summon_Timer -= diff; + + if (Vanish_Timer < diff) + { + //Invisble Model + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,11686); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + //m_creature->CombatStop(); + DoResetThreat(); + VanishedOnce = true; + Vanish_Timer = 45000; + Visible_Timer = 6000; + }else Vanish_Timer -= diff; + + if (VanishedOnce) + { + if(Visible_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + //The Panther Model + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,15215); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if (!PhaseTwo) + { + DoYell(SAY_TRANSFORM,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_TRANSFORM); + } + + const CreatureInfo *cinfo = m_creature->GetCreatureInfo(); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35))); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35))); + m_creature->UpdateDamagePhysical(BASE_ATTACK); + DoStartAttackAndMovement(target); + //The Panther Model + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,15215); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + PhaseTwo = true; + }else Visible_Timer -= diff; + } + + //Cleave_Timer + if(PhaseTwo && Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_CLEAVE); + Cleave_Timer = 16000; + }Cleave_Timer -=diff; + + //Gouge_Timer + if(PhaseTwo && Gouge_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_GOUGE); + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-80); + + Gouge_Timer = 17000+rand()%10000; + }else Gouge_Timer -= diff; + + DoMeleeAttackIfReady(); + } + } +}; +CreatureAI* GetAI_boss_arlokk(Creature *_Creature) +{ + return new boss_arlokkAI (_Creature); +} + +void AddSC_boss_arlokk() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_arlokk"; + newscript->GetAI = GetAI_boss_arlokk; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_gahzranka.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_gahzranka.cpp new file mode 100644 index 00000000000..6fd39159cb5 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_gahzranka.cpp @@ -0,0 +1,92 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Gahz'ranka +SD%Complete: 85 +SDComment: Massive Geyser with knockback not working. Spell buggy. +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" + +#define SPELL_FROSTBREATH 21099 +#define SPELL_MASSIVEGEYSER 22421 //Not working. Cause its a summon... +#define SPELL_SLAM 24326 + +struct MANGOS_DLL_DECL boss_gahzrankaAI : public ScriptedAI +{ + boss_gahzrankaAI(Creature *c) : ScriptedAI(c) {Reset();} + uint32 Frostbreath_Timer; + uint32 MassiveGeyser_Timer; + uint32 Slam_Timer; + + void Reset() + { + Frostbreath_Timer = 8000; + MassiveGeyser_Timer = 25000; + Slam_Timer = 17000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Frostbreath_Timer + if (Frostbreath_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_FROSTBREATH); + Frostbreath_Timer = 7000 + rand()%4000; + }else Frostbreath_Timer -= diff; + + //MassiveGeyser_Timer + if (MassiveGeyser_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MASSIVEGEYSER); + DoResetThreat(); + + MassiveGeyser_Timer = 22000 + rand()%10000; + }else MassiveGeyser_Timer -= diff; + + //Slam_Timer + if (Slam_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SLAM); + Slam_Timer = 12000 + rand()%8000; + }else Slam_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_gahzranka(Creature *_Creature) +{ + return new boss_gahzrankaAI (_Creature); +} + +void AddSC_boss_gahzranka() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_gahzranka"; + newscript->GetAI = GetAI_boss_gahzranka; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_grilek.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_grilek.cpp new file mode 100644 index 00000000000..b25843dbb24 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_grilek.cpp @@ -0,0 +1,92 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Grilek +SD%Complete: 100 +SDComment: +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_AVARTAR 24646 //The Enrage Spell +#define SPELL_GROUNDTREMOR 6524 + +struct MANGOS_DLL_DECL boss_grilekAI : public ScriptedAI +{ + boss_grilekAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Avartar_Timer; + uint32 GroundTremor_Timer; + + void Reset() + { + Avartar_Timer = 15000 + rand()%10000; + GroundTremor_Timer = 8000 + rand()%8000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Avartar_Timer + if (Avartar_Timer < diff) + { + + DoCast(m_creature, SPELL_AVARTAR); + Unit* target = NULL; + + target = SelectUnit(SELECT_TARGET_RANDOM,1); + + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-50); + if (target) + DoStartAttackAndMovement(target); + + Avartar_Timer = 25000 + rand()%10000; + }else Avartar_Timer -= diff; + + //GroundTremor_Timer + if (GroundTremor_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_GROUNDTREMOR); + GroundTremor_Timer = 12000 + rand()%4000; + }else GroundTremor_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_grilek(Creature *_Creature) +{ + return new boss_grilekAI (_Creature); +} + +void AddSC_boss_grilek() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_grilek"; + newscript->GetAI = GetAI_boss_grilek; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_hakkar.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_hakkar.cpp new file mode 100644 index 00000000000..719f2d77b87 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_hakkar.cpp @@ -0,0 +1,256 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Hakkar +SD%Complete: 95 +SDComment: Blood siphon spell buggy cause of Core Issue. +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_BLOODSIPHON 24322 +#define SPELL_CORRUPTEDBLOOD 24328 +#define SPELL_CAUSEINSANITY 24327 //Not working disabled. +#define SPELL_WILLOFHAKKAR 24178 +#define SPELL_ENRAGE 24318 + +// The Aspects of all High Priests +#define SPELL_ASPECT_OF_JEKLIK 24687 +#define SPELL_ASPECT_OF_VENOXIS 24688 +#define SPELL_ASPECT_OF_MARLI 24686 +#define SPELL_ASPECT_OF_THEKAL 24689 +#define SPELL_ASPECT_OF_ARLOKK 24690 + +#define SAY_AGGRO "PRIDE HERALDS THE END OF YOUR WORLD. COME, MORTALS! FACE THE WRATH OF THE SOULFLAYER!" +#define SOUND_AGGRO 8414 + +#define SAY_SLAY "Fleeing will do you no good, mortals!" + +struct MANGOS_DLL_DECL boss_hakkarAI : public ScriptedAI +{ + boss_hakkarAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + uint32 BloodSiphon_Timer; + uint32 CorruptedBlood_Timer; + uint32 CauseInsanity_Timer; + uint32 WillOfHakkar_Timer; + uint32 Enrage_Timer; + + uint32 CheckJeklik_Timer; + uint32 CheckVenoxis_Timer; + uint32 CheckMarli_Timer; + uint32 CheckThekal_Timer; + uint32 CheckArlokk_Timer; + + uint32 AspectOfJeklik_Timer; + uint32 AspectOfVenoxis_Timer; + uint32 AspectOfMarli_Timer; + uint32 AspectOfThekal_Timer; + uint32 AspectOfArlokk_Timer; + + ScriptedInstance *pInstance; + + bool Enraged; + + void Reset() + { + BloodSiphon_Timer = 90000; + CorruptedBlood_Timer = 25000; + CauseInsanity_Timer = 17000; + WillOfHakkar_Timer = 17000; + Enrage_Timer = 600000; + + CheckJeklik_Timer = 1000; + CheckVenoxis_Timer = 2000; + CheckMarli_Timer = 3000; + CheckThekal_Timer = 4000; + CheckArlokk_Timer = 5000; + + AspectOfJeklik_Timer = 4000; + AspectOfVenoxis_Timer = 7000; + AspectOfMarli_Timer = 12000; + AspectOfThekal_Timer = 8000; + AspectOfArlokk_Timer = 18000; + + Enraged = false; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //BloodSiphon_Timer + if (BloodSiphon_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_BLOODSIPHON); + BloodSiphon_Timer = 90000; + }else BloodSiphon_Timer -= diff; + + //CorruptedBlood_Timer + if (CorruptedBlood_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CORRUPTEDBLOOD); + CorruptedBlood_Timer = 30000 + rand()%15000; + }else CorruptedBlood_Timer -= diff; + + //CauseInsanity_Timer + // if (CauseInsanity_Timer < diff) + // { + // + // Unit* target = NULL; + // target = SelectUnit(SELECT_TARGET_RANDOM,0); + + // DoCast(target,SPELL_CAUSEINSANITY); + + // CauseInsanity_Timer = 35000 + rand()%8000; + // }else CauseInsanity_Timer -= diff; + + //WillOfHakkar_Timer + if (WillOfHakkar_Timer < diff) + { + + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(target,SPELL_WILLOFHAKKAR); + WillOfHakkar_Timer = 25000 + rand()%10000; + }else WillOfHakkar_Timer -= diff; + + if (!Enraged && Enrage_Timer < diff) + { + DoCast(m_creature, SPELL_ENRAGE); + Enraged = true; + }else Enrage_Timer -= diff; + + //Checking if Jeklik is dead. If not we cast her Aspect + if(CheckJeklik_Timer < diff) + { + if(pInstance) + { + if(!pInstance->GetData(DATA_JEKLIKISDEAD)) + { + if (AspectOfJeklik_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ASPECT_OF_JEKLIK); + AspectOfJeklik_Timer = 10000 + rand()%4000; + }else AspectOfJeklik_Timer -= diff; + } + } + CheckJeklik_Timer = 1000; + }else CheckJeklik_Timer -= diff; + + //Checking if Venoxis is dead. If not we cast his Aspect + if(CheckVenoxis_Timer < diff) + { + if(pInstance) + { + if(!pInstance->GetData(DATA_VENOXISISDEAD)) + { + if (AspectOfVenoxis_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ASPECT_OF_VENOXIS); + AspectOfVenoxis_Timer = 8000; + }else AspectOfVenoxis_Timer -= diff; + } + } + CheckVenoxis_Timer = 1000; + }else CheckVenoxis_Timer -= diff; + + //Checking if Marli is dead. If not we cast her Aspect + if(CheckMarli_Timer < diff) + { + if(pInstance) + { + if(!pInstance->GetData(DATA_MARLIISDEAD)) + { + if (AspectOfMarli_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ASPECT_OF_MARLI); + AspectOfMarli_Timer = 10000; + }else AspectOfMarli_Timer -= diff; + + } + } + CheckMarli_Timer = 1000; + }else CheckMarli_Timer -= diff; + + //Checking if Thekal is dead. If not we cast his Aspect + if(CheckThekal_Timer < diff) + { + if(pInstance) + { + if(!pInstance->GetData(DATA_THEKALISDEAD)) + { + if (AspectOfThekal_Timer < diff) + { + DoCast(m_creature,SPELL_ASPECT_OF_THEKAL); + AspectOfThekal_Timer = 15000; + }else AspectOfThekal_Timer -= diff; + } + } + CheckThekal_Timer = 1000; + }else CheckThekal_Timer -= diff; + + //Checking if Arlokk is dead. If yes we cast her Aspect + if(CheckArlokk_Timer < diff) + { + if(pInstance) + { + if(!pInstance->GetData(DATA_ARLOKKISDEAD)) + { + if (AspectOfArlokk_Timer < diff) + { + DoCast(m_creature,SPELL_ASPECT_OF_ARLOKK); + DoResetThreat(); + + AspectOfArlokk_Timer = 10000 + rand()%5000; + }else AspectOfArlokk_Timer -= diff; + } + } + CheckArlokk_Timer = 1000; + }else CheckArlokk_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_hakkar(Creature *_Creature) +{ + return new boss_hakkarAI (_Creature); +} + +void AddSC_boss_hakkar() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_hakkar"; + newscript->GetAI = GetAI_boss_hakkar; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_hazzarah.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_hazzarah.cpp new file mode 100644 index 00000000000..adf5a44ec95 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_hazzarah.cpp @@ -0,0 +1,100 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Hazzarah +SD%Complete: 100 +SDComment: +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_MANABURN 26046 +#define SPELL_SLEEP 24664 + +struct MANGOS_DLL_DECL boss_hazzarahAI : public ScriptedAI +{ + boss_hazzarahAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 ManaBurn_Timer; + uint32 Sleep_Timer; + uint32 Illusions_Timer; + Creature* Illusion; + + void Reset() + { + ManaBurn_Timer = 4000 + rand()%6000; + Sleep_Timer = 10000 + rand()%8000; + Illusions_Timer = 10000 + rand()%8000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //ManaBurn_Timer + if (ManaBurn_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MANABURN); + ManaBurn_Timer = 8000 + rand()%8000; + }else ManaBurn_Timer -= diff; + + //Sleep_Timer + if (Sleep_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SLEEP); + Sleep_Timer = 12000 + rand()%8000; + }else Sleep_Timer -= diff; + + //Illusions_Timer + if (Illusions_Timer < diff) + { + //We will summon 3 illusions that will spawn on a random gamer and attack this gamer + //We will just use one model for the beginning + Unit* target = NULL; + for(int i = 0; i < 3;i++) + { + target = SelectUnit(SELECT_TARGET_RANDOM,0); + Illusion = m_creature->SummonCreature(15163,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,30000); + ((CreatureAI*)Illusion->AI())->AttackStart(target); + } + + Illusions_Timer = 15000 + rand()%10000; + }else Illusions_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_hazzarah(Creature *_Creature) +{ + return new boss_hazzarahAI (_Creature); +} + +void AddSC_boss_hazzarah() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_hazzarah"; + newscript->GetAI = GetAI_boss_hazzarah; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_jeklik.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_jeklik.cpp new file mode 100644 index 00000000000..efe76bbe5ec --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_jeklik.cpp @@ -0,0 +1,297 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Jeklik +SD%Complete: 85 +SDComment: Problem in finding the right flying batriders for spawning and making them fly. +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_CHARGE 22911 +#define SPELL_SONICBURST 23918 +#define SPELL_SCREECH 6605 +#define SPELL_SHADOW_WORD_PAIN 23952 +#define SPELL_MIND_FLAY 23953 +#define SPELL_CHAIN_MIND_FLAY 26044 //Right ID unknown. So disabled +#define SPELL_GREATERHEAL 23954 +#define SPELL_BAT_FORM 23966 + +// Batriders Spell + +#define SPELL_BOMB 40332 //Wrong ID but Magmadars bomb is not working... + +#define SAY_AGGRO "Lord Hireek grant me wings of vengance!" +#define SAY_DEATH "Hireek - Finnaly death. Curse you Hakkar! Curse you!" + +#define SOUND_AGGRO 8417 +#define SOUND_DEATH 8422 + +struct MANGOS_DLL_DECL boss_jeklikAI : public ScriptedAI +{ + boss_jeklikAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Charge_Timer; + uint32 SonicBurst_Timer; + uint32 Screech_Timer; + uint32 SpawnBats_Timer; + uint32 ShadowWordPain_Timer; + uint32 MindFlay_Timer; + uint32 ChainMindFlay_Timer; + uint32 GreaterHeal_Timer; + uint32 SpawnFlyingBats_Timer; + + bool PhaseTwo; + + void Reset() + { + Charge_Timer = 20000; + SonicBurst_Timer = 8000; + Screech_Timer = 13000; + SpawnBats_Timer = 60000; + ShadowWordPain_Timer = 6000; + MindFlay_Timer = 11000; + ChainMindFlay_Timer = 26000; + GreaterHeal_Timer = 50000; + SpawnFlyingBats_Timer = 10000; + + PhaseTwo = false; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + DoCast(m_creature,SPELL_BAT_FORM); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + + ScriptedInstance *pInstance = ((ScriptedInstance*)m_creature->GetInstanceData()); + if(pInstance) + pInstance->SetData(DATA_JEKLIK_DEATH, 0); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget()) + return; + + if( m_creature->getVictim() && m_creature->isAlive()) + { + if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() > 50)) + { + if (Charge_Timer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0); + if(target) + { + DoCast(target,SPELL_CHARGE); + + m_creature->Relocate(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0); + m_creature->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true,1); + DoStartAttackAndMovement(target); + } + + Charge_Timer = 15000 + rand()%15000; + }else Charge_Timer -= diff; + + if (SonicBurst_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SONICBURST); + SonicBurst_Timer = 8000 + rand()%5000; + }else SonicBurst_Timer -= diff; + + if (Screech_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SCREECH); + Screech_Timer = 18000 + rand()%8000; + }else Screech_Timer -= diff; + + if (SpawnBats_Timer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0); + + Creature* Bat = NULL; + Bat = m_creature->SummonCreature(11368,-12291.6220,-1380.2640,144.8304,5.483, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Bat ) { Bat ->AI()->AttackStart(target); } + + Bat = m_creature->SummonCreature(11368,-12289.6220,-1380.2640,144.8304,5.483, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Bat ) { Bat ->AI()->AttackStart(target); } + + Bat = m_creature->SummonCreature(11368,-12293.6220,-1380.2640,144.8304,5.483, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Bat ) { Bat ->AI()->AttackStart(target); } + + Bat = m_creature->SummonCreature(11368,-12291.6220,-1380.2640,144.8304,5.483, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Bat ) { Bat ->AI()->AttackStart(target); } + + Bat = m_creature->SummonCreature(11368,-12289.6220,-1380.2640,144.8304,5.483, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Bat ) { Bat ->AI()->AttackStart(target); } + Bat = m_creature->SummonCreature(11368,-12293.6220,-1380.2640,144.8304,5.483, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Bat ) { Bat ->AI()->AttackStart(target); } + + SpawnBats_Timer = 60000; + }else SpawnBats_Timer -= diff; + } + else + { + if(PhaseTwo) + { + if(PhaseTwo && ShadowWordPain_Timer < diff) + { + Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0); + if(target) + { + DoCast(target, SPELL_SHADOW_WORD_PAIN); + ShadowWordPain_Timer = 12000 + rand()%6000; + } + }ShadowWordPain_Timer -=diff; + + if(MindFlay_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_MIND_FLAY); + MindFlay_Timer = 16000; + }MindFlay_Timer -=diff; + + if(ChainMindFlay_Timer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature->getVictim(), SPELL_CHAIN_MIND_FLAY); + ChainMindFlay_Timer = 15000 + rand()%15000; + }ChainMindFlay_Timer -=diff; + + if(GreaterHeal_Timer < diff) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature,SPELL_GREATERHEAL); + GreaterHeal_Timer = 25000 + rand()%10000; + }GreaterHeal_Timer -=diff; + + if(SpawnFlyingBats_Timer < diff) + { + Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0); + + Creature* FlyingBat = m_creature->SummonCreature(14965, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()+15, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(FlyingBat) + { + if(target) + FlyingBat->AI()->AttackStart(target); + } + + SpawnFlyingBats_Timer = 10000 + rand()%5000; + }SpawnFlyingBats_Timer -=diff; + } + else + { + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,15219); + DoResetThreat(); + PhaseTwo = true; + } + } + + DoMeleeAttackIfReady(); + } + } +}; + +//Flying Bat +struct MANGOS_DLL_DECL mob_batriderAI : public ScriptedAI +{ + mob_batriderAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + ScriptedInstance *pInstance; + + uint32 Bomb_Timer; + uint32 Check_Timer; + + void Reset() + { + Bomb_Timer = 2000; + Check_Timer = 1000; + + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit *who) {} + + void UpdateAI (const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Bomb_Timer + if(Bomb_Timer < diff) + { + Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0); + if(target) + { + DoCast(target, SPELL_BOMB); + Bomb_Timer = 5000; + } + }else Bomb_Timer -= diff; + + //Check_Timer + if(Check_Timer < diff) + { + if(pInstance) + { + if(pInstance->GetData(DATA_JEKLIKISDEAD)) + { + m_creature->setDeathState(JUST_DIED); + m_creature->RemoveCorpse(); + } + } + + Check_Timer = 1000; + }else Check_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_jeklik(Creature *_Creature) +{ + return new boss_jeklikAI (_Creature); +} + +CreatureAI* GetAI_mob_batrider(Creature *_Creature) +{ + return new mob_batriderAI (_Creature); +} + +void AddSC_boss_jeklik() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_jeklik"; + newscript->GetAI = GetAI_boss_jeklik; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_batrider"; + newscript->GetAI = GetAI_mob_batrider; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_jindo.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_jindo.cpp new file mode 100644 index 00000000000..8a9a5cb2cc8 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_jindo.cpp @@ -0,0 +1,308 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Jin'do the Hexxer +SD%Complete: 85 +SDComment: Mind Control not working because of core bug. Shades visible for all. +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_BRAINWASHTOTEM 24262 +#define SPELL_POWERFULLHEALINGWARD 24309 //We will not use this spell. We will summon a totem by script cause the spell totems will not cast. +#define SPELL_HEX 24053 +#define SPELL_DELUSIONSOFJINDO 24306 +#define SPELL_SHADEOFJINDO 24308 //We will not use this spell. We will summon a shade by script. + +//Healing Ward Spell +#define SPELL_HEAL 38588 //Totems are not working right. Right heal spell ID is 24311 but this spell is not casting... + +//Shade of Jindo Spell +#define SPELL_SHADOWSHOCK 19460 +#define SPELL_INVISIBLE 24699 + +#define SAY_AGGRO "Welcome to da great show friends! Step right up to die!" + +#define SOUND_AGGRO 8425 + +struct MANGOS_DLL_DECL boss_jindoAI : public ScriptedAI +{ + boss_jindoAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Reset(); + } + + uint32 BrainWashTotem_Timer; + uint32 HealingWard_Timer; + uint32 Hex_Timer; + uint32 Delusions_Timer; + uint32 Teleport_Timer; + + Creature *Shade; + Creature *Skeletons; + Creature *HealingWard; + + ScriptedInstance *pInstance; + + void Reset() + { + BrainWashTotem_Timer = 20000; + HealingWard_Timer = 16000; + Hex_Timer = 8000; + Delusions_Timer = 10000; + Teleport_Timer = 5000; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //BrainWashTotem_Timer + if (BrainWashTotem_Timer < diff) + { + DoCast(m_creature, SPELL_BRAINWASHTOTEM); + BrainWashTotem_Timer = 18000 + rand()%8000; + }else BrainWashTotem_Timer -= diff; + + //HealingWard_Timer + if (HealingWard_Timer < diff) + { + //DoCast(m_creature, SPELL_POWERFULLHEALINGWARD); + HealingWard = m_creature->SummonCreature(14987, m_creature->GetPositionX()+3, m_creature->GetPositionY()-2, m_creature->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,30000); + HealingWard_Timer = 14000 + rand()%6000; + }else HealingWard_Timer -= diff; + + //Hex_Timer + if (Hex_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_HEX); + + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-80); + + Hex_Timer = 12000 + rand()%8000; + }else Hex_Timer -= diff; + + //Casting the delusion curse with a shade. So shade will attack the same target with the curse. + if (Delusions_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(target, SPELL_DELUSIONSOFJINDO); + + Shade = m_creature->SummonCreature(14986, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + Shade->AI()->AttackStart(target); + + Delusions_Timer = 4000 + rand()%8000; + }else Delusions_Timer -= diff; + + //Teleporting a random gamer and spawning 9 skeletons that will attack this gamer + if (Teleport_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target && target->GetTypeId() == TYPEID_PLAYER) + { + DoTeleportPlayer(target, -11583.7783,-1249.4278,77.5471,4.745); + + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(target,-100); + + Skeletons = m_creature->SummonCreature(14826, target->GetPositionX()+2, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + Skeletons->AI()->AttackStart(target); + Skeletons = m_creature->SummonCreature(14826, target->GetPositionX()-2, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + Skeletons->AI()->AttackStart(target); + Skeletons = m_creature->SummonCreature(14826, target->GetPositionX()+4, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + Skeletons->AI()->AttackStart(target); + Skeletons = m_creature->SummonCreature(14826, target->GetPositionX()-4, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + Skeletons->AI()->AttackStart(target); + Skeletons = m_creature->SummonCreature(14826, target->GetPositionX(), target->GetPositionY()+2, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + Skeletons->AI()->AttackStart(target); + Skeletons = m_creature->SummonCreature(14826, target->GetPositionX(), target->GetPositionY()-2, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + Skeletons->AI()->AttackStart(target); + Skeletons = m_creature->SummonCreature(14826, target->GetPositionX(), target->GetPositionY()+4, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + Skeletons->AI()->AttackStart(target); + Skeletons = m_creature->SummonCreature(14826, target->GetPositionX(), target->GetPositionY()-4, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + Skeletons->AI()->AttackStart(target); + Skeletons = m_creature->SummonCreature(14826, target->GetPositionX()+3, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + Skeletons->AI()->AttackStart(target); + } + + Teleport_Timer = 15000 + rand()%8000; + }else Teleport_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Healing Ward +struct MANGOS_DLL_DECL mob_healing_wardAI : public ScriptedAI +{ + mob_healing_wardAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Reset(); + } + + uint32 Heal_Timer; + + ScriptedInstance *pInstance; + + void Reset() + { + Heal_Timer = 2000; + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + } + } + } + + void UpdateAI (const uint32 diff) + { + //Heal_Timer + if(Heal_Timer < diff) + { + if(pInstance) + { + Unit *pJindo = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_JINDO)); + DoCast(pJindo, SPELL_HEAL); + } + Heal_Timer = 3000; + }else Heal_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +//Shade of Jindo +struct MANGOS_DLL_DECL mob_shade_of_jindoAI : public ScriptedAI +{ + mob_shade_of_jindoAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Reset(); + } + + uint32 ShadowShock_Timer; + + ScriptedInstance *pInstance; + + void Reset() + { + ShadowShock_Timer = 1000; + m_creature->CastSpell(m_creature, SPELL_INVISIBLE,true); + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + } + } + } + + void UpdateAI (const uint32 diff) + { + + //ShadowShock_Timer + if(ShadowShock_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SHADOWSHOCK); + ShadowShock_Timer = 2000; + }else ShadowShock_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_jindo(Creature *_Creature) +{ + return new boss_jindoAI (_Creature); +} + +CreatureAI* GetAI_mob_healing_ward(Creature *_Creature) +{ + return new mob_healing_wardAI (_Creature); +} + +CreatureAI* GetAI_mob_shade_of_jindo(Creature *_Creature) +{ + return new mob_shade_of_jindoAI (_Creature); +} + +void AddSC_boss_jindo() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_jindo"; + newscript->GetAI = GetAI_boss_jindo; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_healing_ward"; + newscript->GetAI = GetAI_mob_healing_ward; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_shade_of_jindo"; + newscript->GetAI = GetAI_mob_shade_of_jindo; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_mandokir.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_mandokir.cpp new file mode 100644 index 00000000000..7d8465b2780 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_mandokir.cpp @@ -0,0 +1,311 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Mandokir +SD%Complete: 90 +SDComment: Ohgan function needs improvements. +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_CHARGE 24315 +#define SPELL_CLEAVE 20691 +#define SPELL_FEAR 29321 +#define SPELL_WHIRLWIND 24236 +#define SPELL_MORTAL_STRIKE 24573 +#define SPELL_ENRAGE 23537 +#define SPELL_WATCH 24314 +#define SPELL_LEVEL_UP 24312 + +//Ohgans Spells +#define SPELL_SUNDERARMOR 24317 + +#define SAY_AGGRO "I'll feed your souls to Hakkar himself!" +#define SOUND_AGGRO 8413 + +#define SAY_WATCH "I'm keeping my eye on you, $N!" +#define SAY_KILL "DING!" + +struct MANGOS_DLL_DECL boss_mandokirAI : public ScriptedAI +{ + boss_mandokirAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + uint32 Watch_Timer; + uint32 TargetInRange; + uint32 Cleave_Timer; + uint32 Whirlwind_Timer; + uint32 Fear_Timer; + uint32 MortalStrike_Timer; + uint32 Check_Timer; + float targetX; + float targetY; + float targetZ; + + ScriptedInstance *pInstance; + + bool endWatch; + bool someWatched; + bool RaptorDead; + bool CombatStart; + + uint64 WatchTarget; + + void Reset() + { + Watch_Timer = 33000; + Cleave_Timer = 7000; + Whirlwind_Timer = 20000; + Fear_Timer = 1000; + MortalStrike_Timer = 1000; + Check_Timer = 1000; + + targetX = 0.0; + targetY = 0.0; + targetZ = 0.0; + TargetInRange = 0; + + WatchTarget = 0; + + someWatched = false; + endWatch = false; + RaptorDead = false; + CombatStart = false; + + DoCast(m_creature, 23243); + } + + void KilledUnit(Unit* victim) + { + if(victim->GetTypeId() == TYPEID_PLAYER) + { + DoYell(SAY_KILL, LANG_UNIVERSAL, NULL); + DoCast(m_creature, SPELL_LEVEL_UP, true); + } + } + + void Aggro(Unit *who) {} + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget()) + return; + + if( m_creature->getVictim() && m_creature->isAlive()) + { + if(!CombatStart) + { + //At combat Start Mandokir is mounted so we must unmount it first + m_creature->Unmount(); + + //And summon his raptor + m_creature->SummonCreature(14988, m_creature->getVictim()->GetPositionX(), m_creature->getVictim()->GetPositionY(), m_creature->getVictim()->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 35000); + CombatStart = true; + } + + if (Watch_Timer < diff) //Every 20 Sec Mandokir will check this + { + if(WatchTarget) //If someone is watched and If the Position of the watched target is different from the one stored, or are attacking, mandokir will charge him + { + Unit* pUnit = Unit::GetUnit(*m_creature, WatchTarget); + + if( pUnit && ( + targetX != pUnit->GetPositionX() || + targetY != pUnit->GetPositionY() || + targetZ != pUnit->GetPositionZ() || + pUnit->isInCombat())) + { + if(m_creature->IsWithinDistInMap(pUnit, ATTACK_DISTANCE)) + { + DoCast(pUnit,24316); + } + else + { + DoCast(pUnit,SPELL_CHARGE); + m_creature->SendMonsterMove(pUnit->GetPositionX(), pUnit->GetPositionY(), pUnit->GetPositionZ(), 0, true,1); + DoStartAttackAndMovement(pUnit); + } + } + } + someWatched = false; + Watch_Timer = 20000; + }else Watch_Timer -= diff; + + if ((Watch_Timer < 8000) && !someWatched) //8 sec(cast time + expire time) before the check for the watch effect mandokir will cast watch debuff on a random target + { + Unit* p = SelectUnit(SELECT_TARGET_RANDOM,0); + if(p) + { + DoYell(SAY_WATCH, LANG_UNIVERSAL, p); + DoCast(p, SPELL_WATCH); + WatchTarget = p->GetGUID(); + someWatched = true; + endWatch = true; + } + } + + if ((Watch_Timer < 1000) && endWatch) //1 sec before the debuf expire, store the target position + { + Unit* pUnit = Unit::GetUnit(*m_creature, WatchTarget); + if (pUnit) + { + targetX = pUnit->GetPositionX(); + targetY = pUnit->GetPositionY(); + targetZ = pUnit->GetPositionZ(); + } + endWatch = false; + } + + if(!someWatched) + { + //Cleave + if (Cleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_CLEAVE); + Cleave_Timer = 7000; + }else Cleave_Timer -= diff; + + //Whirlwind + if (Whirlwind_Timer < diff) + { + DoCast(m_creature,SPELL_WHIRLWIND); + Whirlwind_Timer = 18000; + }else Whirlwind_Timer -= diff; + + //If more then 3 targets in melee range mandokir will cast fear + if (Fear_Timer < diff) + { + TargetInRange = 0; + + std::list::iterator i = m_creature->getThreatManager().getThreatList().begin(); + for(; i != m_creature->getThreatManager().getThreatList().end(); ++i) + { + Unit* pUnit = Unit::GetUnit(*m_creature, (*i)->getUnitGuid()); + if(pUnit && m_creature->IsWithinDistInMap(pUnit, ATTACK_DISTANCE)) + TargetInRange++; + } + + if(TargetInRange > 3) + DoCast(m_creature->getVictim(),SPELL_FEAR); + + Fear_Timer = 4000; + }else Fear_Timer -=diff; + + //Mortal Strike if target below 50% hp + if (m_creature->getVictim()->GetHealth() < m_creature->getVictim()->GetMaxHealth()*0.5) + { + if (MortalStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MORTAL_STRIKE); + MortalStrike_Timer = 15000; + }else MortalStrike_Timer -= diff; + } + } + //Checking if Ohgan is dead. If yes Mandokir will enrage. + if(Check_Timer < diff) + { + if(pInstance) + { + if(pInstance->GetData(DATA_OHGANISDEAD)) + { + if (!RaptorDead) + { + DoCast(m_creature, SPELL_ENRAGE); + RaptorDead = true; + } + } + } + + Check_Timer = 1000; + }else Check_Timer -= diff; + + DoMeleeAttackIfReady(); + } + } +}; + +//Ohgan +struct MANGOS_DLL_DECL mob_ohganAI : public ScriptedAI +{ + mob_ohganAI(Creature *c) : ScriptedAI(c) + { + pInstance = ((ScriptedInstance*)c->GetInstanceData()); + Reset(); + } + + uint32 SunderArmor_Timer; + ScriptedInstance *pInstance; + + void Reset() + { + SunderArmor_Timer = 5000; + } + + void Aggro(Unit *who) {} + + void JustDied(Unit* Killer) + { + if(pInstance) + pInstance->SetData(DATA_OHGAN_DEATH, 0); + } + + void UpdateAI (const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //SunderArmor_Timer + if(SunderArmor_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_SUNDERARMOR); + SunderArmor_Timer = 10000 + rand()%5000; + }else SunderArmor_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_mandokir(Creature *_Creature) +{ + return new boss_mandokirAI (_Creature); +} + +CreatureAI* GetAI_mob_ohgan(Creature *_Creature) +{ + return new mob_ohganAI (_Creature); +} + +void AddSC_boss_mandokir() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_mandokir"; + newscript->GetAI = GetAI_boss_mandokir; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_ohgan"; + newscript->GetAI = GetAI_mob_ohgan; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_marli.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_marli.cpp new file mode 100644 index 00000000000..44bfc0973e8 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_marli.cpp @@ -0,0 +1,269 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Marli +SD%Complete: 80 +SDComment: Charging healers and casters not working. Perhaps wrong Spell Timers. +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_CHARGE 22911 +#define SPELL_ASPECT_OF_MARLI 24686 // A stun spell +#define SPELL_ENVOLWINGWEB 24110 +#define SPELL_POISONVOLLEY 24099 +#define SPELL_SPIDER_FORM 24084 + +//The Spider Spells +#define SPELL_LEVELUP 24312 //Not right Spell. + +#define SAY_AGGRO "Draw me to your web mistress Shadra. Unleash your venom!" +#define SOUND_AGGRO 8418 + +#define SAY_DEATH "ShadraDeath - Bless you mortal for this release. Hakkar controls me no longer..." +#define SOUND_DEATH 8423 + +struct MANGOS_DLL_DECL boss_marliAI : public ScriptedAI +{ + boss_marliAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 SpawnStartSpiders_Timer; + uint32 PoisonVolley_Timer; + uint32 SpawnSpider_Timer; + uint32 Charge_Timer; + uint32 Aspect_Timer; + uint32 Transform_Timer; + uint32 TransformBack_Timer; + + Creature *Spider; + bool Spawned; + bool PhaseTwo; + + void Reset() + { + SpawnStartSpiders_Timer = 1000; + PoisonVolley_Timer = 15000; + SpawnSpider_Timer = 30000; + Charge_Timer = 1500; + Aspect_Timer = 12000; + Transform_Timer = 45000; + TransformBack_Timer = 25000; + + Spawned = false; + PhaseTwo = false; + + m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); + m_creature->ApplySpellImmune(1, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + if(pInstance) + pInstance->SetData(DATA_MARLI_DEATH, 0); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget()) + return; + + if( m_creature->getVictim() && m_creature->isAlive()) + { + if (PoisonVolley_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_POISONVOLLEY); + PoisonVolley_Timer = 10000 + rand()%10000; + }else PoisonVolley_Timer -= diff; + + if (!PhaseTwo && Aspect_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_ASPECT_OF_MARLI); + Aspect_Timer = 13000 + rand()%5000; + }else Aspect_Timer -= diff; + + if (!Spawned && SpawnStartSpiders_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + Spider = m_creature->SummonCreature(15041,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Spider ) { Spider ->AI()->AttackStart(target); } + Spider = m_creature->SummonCreature(15041,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Spider ) { Spider ->AI()->AttackStart(target); } + Spider = m_creature->SummonCreature(15041,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Spider ) { Spider ->AI()->AttackStart(target); } + Spider = m_creature->SummonCreature(15041,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Spider ) { Spider ->AI()->AttackStart(target); } + + Spawned = true; + }else SpawnStartSpiders_Timer -= diff; + + if (SpawnSpider_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + Spider = m_creature->SummonCreature(15041,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000); + if(target && Spider ) { Spider ->AI()->AttackStart(target); } + + SpawnSpider_Timer = 12000 + rand()%5000; + }else SpawnSpider_Timer -= diff; + + if(!PhaseTwo && Transform_Timer < diff) + { + DoCast(m_creature,SPELL_SPIDER_FORM); + const CreatureInfo *cinfo = m_creature->GetCreatureInfo(); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 35))); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35))); + m_creature->UpdateDamagePhysical(BASE_ATTACK); + DoCast(m_creature->getVictim(),SPELL_ENVOLWINGWEB); + + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-100); + + PhaseTwo = true; + Transform_Timer = 35000 + rand()%25000; + }else Transform_Timer -= diff; + + if (PhaseTwo) + { + if (Charge_Timer < diff) + { + Unit* target = NULL; + int i = 0 ; + while (i < 3) // max 3 tries to get a random target with power_mana + { + ++i; //not aggro leader + target = SelectUnit(SELECT_TARGET_RANDOM,1); + if (target) + if (target->getPowerType() == POWER_MANA) + i=3; + } + if (target) + DoCast(target, SPELL_CHARGE); + // m_creature->Relocate(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0); + // m_creature->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true,1); + DoStartAttackAndMovement(target); + + Charge_Timer = 8000; + }else Charge_Timer -= diff; + + if (TransformBack_Timer < diff) + { + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,15220); + const CreatureInfo *cinfo = m_creature->GetCreatureInfo(); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 1))); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 1))); + m_creature->UpdateDamagePhysical(BASE_ATTACK); + + PhaseTwo = false; + TransformBack_Timer = 25000 + rand()%15000; + }else TransformBack_Timer -= diff; + + } + + DoMeleeAttackIfReady(); + } + } +}; + +//Spawn of Marli +struct MANGOS_DLL_DECL mob_spawn_of_marliAI : public ScriptedAI +{ + mob_spawn_of_marliAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 LevelUp_Timer; + + void Reset() + { + LevelUp_Timer = 3000; + } + + void Aggro(Unit *who) + { + } + + void MoveInLineOfSight(Unit *who) + { + if (!who || m_creature->getVictim()) + return; + + if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who)) + { + float attackRadius = m_creature->GetAttackDistance(who); + if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who)) + { + if(who->HasStealthAura()) + who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + + DoStartAttackAndMovement(who); + } + } + } + + void UpdateAI (const uint32 diff) + { + //Return since we have no target + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //LevelUp_Timer + if(LevelUp_Timer < diff) + { + DoCast(m_creature,SPELL_LEVELUP); + LevelUp_Timer = 3000; + }else LevelUp_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_marli(Creature *_Creature) +{ + return new boss_marliAI (_Creature); +} + +CreatureAI* GetAI_mob_spawn_of_marli(Creature *_Creature) +{ + return new mob_spawn_of_marliAI (_Creature); +} + +void AddSC_boss_marli() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_marli"; + newscript->GetAI = GetAI_boss_marli; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_spawn_of_marli"; + newscript->GetAI = GetAI_mob_spawn_of_marli; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_renataki.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_renataki.cpp new file mode 100644 index 00000000000..f7e31309b59 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_renataki.cpp @@ -0,0 +1,151 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Renataki +SD%Complete: 100 +SDComment: +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_AMBUSH 24337 +#define SPELL_THOUSANDBLADES 24649 + +struct MANGOS_DLL_DECL boss_renatakiAI : public ScriptedAI +{ + boss_renatakiAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 Invisible_Timer; + uint32 Ambush_Timer; + uint32 Visible_Timer; + uint32 Aggro_Timer; + uint32 ThousandBlades_Timer; + + bool Invisible; + bool Ambushed; + + void Reset() + { + Invisible_Timer = 8000 + rand()%10000; + Ambush_Timer = 3000; + Visible_Timer = 4000; + Aggro_Timer = 15000 + rand()%10000; + ThousandBlades_Timer = 4000 + rand()%4000; + + Invisible = false; + Ambushed = false; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //Invisible_Timer + if (Invisible_Timer < diff) + { + m_creature->InterruptSpell(CURRENT_GENERIC_SPELL); + m_creature->SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 0); + m_creature->SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO , 218171138); + m_creature->SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + 1, 3); + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,11686); + Invisible = true; + + Invisible_Timer = 15000 + rand()%15000; + }else Invisible_Timer -= diff; + + if (Invisible) + { + if (Ambush_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) + { + m_creature->Relocate(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0); + m_creature->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true,1); + DoCast(target,SPELL_AMBUSH); + } + + Ambushed = true; + Ambush_Timer = 3000; + }else Ambush_Timer -= diff; + } + + if (Ambushed) + { + if (Visible_Timer < diff) + { + m_creature->InterruptSpell(CURRENT_GENERIC_SPELL); + m_creature->SetUInt32Value(UNIT_FIELD_DISPLAYID,15268); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetUInt32Value( UNIT_VIRTUAL_ITEM_SLOT_DISPLAY, 31818); + m_creature->SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO , 218171138); + m_creature->SetUInt32Value( UNIT_VIRTUAL_ITEM_INFO + 1, 3); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Invisible = false; + + Visible_Timer = 4000; + }else Visible_Timer -= diff; + } + + //Resetting some aggro so he attacks other gamers + if(!Invisible) + if (Aggro_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,1); + + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-50); + + if (target) + DoStartAttackAndMovement(target); + + Aggro_Timer = 7000 + rand()%13000; + }else Aggro_Timer -= diff; + + if (!Invisible) + if(ThousandBlades_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_THOUSANDBLADES); + ThousandBlades_Timer = 7000 + rand()%5000; + }else ThousandBlades_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_renataki(Creature *_Creature) +{ + return new boss_renatakiAI (_Creature); +} + +void AddSC_boss_renataki() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_renataki"; + newscript->GetAI = GetAI_boss_renataki; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_thekal.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_thekal.cpp new file mode 100644 index 00000000000..91d0d6a290c --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_thekal.cpp @@ -0,0 +1,545 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Thekal +SD%Complete: 95 +SDComment: Almost finished. +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_MORTALCLEAVE 22859 +#define SPELL_SILENCE 23207 +#define SPELL_FRENZY 23342 +#define SPELL_FORCEPUNCH 24189 +#define SPELL_CHARGE 24408 +#define SPELL_ENRAGE 23537 +#define SPELL_SUMMONTIGERS 24183 +#define SPELL_TIGER_FORM 24169 +#define SPELL_RESURRECT 24173 //We will not use this spell. + +//Zealot Lor'Khan Spells +#define SPELL_SHIELD 25020 +#define SPELL_BLOODLUST 24185 +#define SPELL_GREATERHEAL 24208 +#define SPELL_DISARM 22691 + +//Zealot Lor'Khan Spells +#define SPELL_SWEEPINGSTRIKES 18765 +#define SPELL_SINISTERSTRIKE 15667 +#define SPELL_GOUGE 24698 +#define SPELL_KICK 15614 +#define SPELL_BLIND 21060 + +#define SAY_AGGRO "Shirvallah fill me with your rage!" +#define SOUND_AGGRO 8419 + +#define SAY_DEATH "Hakkar binds me no more. Peace at last." +#define SOUND_DEATH 8424 + +struct MANGOS_DLL_DECL boss_thekalAI : public ScriptedAI +{ + boss_thekalAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Reset(); + } + + uint32 MortalCleave_Timer; + uint32 Silence_Timer; + uint32 Frenzy_Timer; + uint32 ForcePunch_Timer; + uint32 Charge_Timer; + uint32 Enrage_Timer; + uint32 SummonTigers_Timer; + uint32 Check_Timer; + uint32 Resurrect_Timer; + + ScriptedInstance *pInstance; + bool Enraged; + bool PhaseTwo; + bool WasDead; + + void Reset() + { + MortalCleave_Timer = 4000; + Silence_Timer = 9000; + Frenzy_Timer = 30000; + ForcePunch_Timer = 4000; + Charge_Timer = 12000; + Enrage_Timer = 32000; + SummonTigers_Timer = 25000; + Check_Timer = 10000; + Resurrect_Timer = 10000; + + Enraged = false; + PhaseTwo = false; + WasDead = false; + + if(pInstance) + pInstance->SetData(DATA_THEKAL_ALIVE, 0); + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void JustDied(Unit* Killer) + { + DoYell(SAY_DEATH,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_DEATH); + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + if(pInstance) + pInstance->SetData(DATA_THEKAL_DEATH, 0); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget()) + return; + + if( m_creature->getVictim() && m_creature->isAlive()) + { + + //Check_Timer for the death of LorKhan and Zath. + if(!WasDead && Check_Timer < diff) + { + if(pInstance) + { + if(pInstance->GetData(DATA_LORKHANISDEAD)) + { + //Resurrect LorKhan + Unit *pLorKhan = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_LORKHAN)); + pLorKhan->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + pLorKhan->setFaction(14); + pLorKhan->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pLorKhan->SetHealth(int(pLorKhan->GetMaxHealth()*1.0)); + } + + if(pInstance->GetData(DATA_ZATHISDEAD)) + { + //Resurrect Zath + Unit *pZath = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_ZATH)); + pZath->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + pZath->setFaction(14); + pZath->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pZath->SetHealth(int(pZath->GetMaxHealth()*1.0)); + } + } + + Check_Timer = 5000; + }else Check_Timer -= diff; + + if (!PhaseTwo && MortalCleave_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_MORTALCLEAVE); + MortalCleave_Timer = 15000 + rand()%5000; + }else MortalCleave_Timer -= diff; + + if (!PhaseTwo && Silence_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SILENCE); + Silence_Timer = 20000 + rand()%5000; + }else Silence_Timer -= diff; + + if (!PhaseTwo && !WasDead && m_creature->GetHealth() <= m_creature->GetMaxHealth() * 0.05) + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 3); + m_creature->AttackStop(); + + if(pInstance) + pInstance->SetData(DATA_THEKALFAKE_DEATH, 0); + + WasDead=true; + } + + //Thekal will transform to Tiger if he died and was not resurrected after 10 seconds. + if(!PhaseTwo && WasDead) + { + if (Resurrect_Timer < diff) + { + DoCast(m_creature,SPELL_TIGER_FORM); + m_creature->SetFloatValue(OBJECT_FIELD_SCALE_X, 2.00f); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetHealth(int(m_creature->GetMaxHealth()*1.0)); + const CreatureInfo *cinfo = m_creature->GetCreatureInfo(); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 40))); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 40))); + m_creature->UpdateDamagePhysical(BASE_ATTACK); + DoResetThreat(); + PhaseTwo = true; + }else Resurrect_Timer -= diff; + } + + if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() == 100) && WasDead) + { + WasDead = false; + } + + if (PhaseTwo) + { + if (Charge_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(target,SPELL_CHARGE); + m_creature->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true,1); + DoStartAttackAndMovement(target); + DoResetThreat(); + + Charge_Timer = 15000 + rand()%7000; + }else Charge_Timer -= diff; + + if (Frenzy_Timer < diff) + { + DoCast(m_creature,SPELL_FRENZY); + Frenzy_Timer = 30000; + }else Frenzy_Timer -= diff; + + if (ForcePunch_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SILENCE); + ForcePunch_Timer = 16000 + rand()%5000; + }else ForcePunch_Timer -= diff; + + if (SummonTigers_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SUMMONTIGERS); + SummonTigers_Timer = 10000 + rand()%4000; + }else SummonTigers_Timer -= diff; + + if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 11) && !Enraged) + { + DoCast(m_creature, SPELL_ENRAGE); + Enraged = true; + } + } + + DoMeleeAttackIfReady(); + } + } +}; + +//Zealot Lor'Khan +struct MANGOS_DLL_DECL mob_zealot_lorkhanAI : public ScriptedAI +{ + mob_zealot_lorkhanAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Reset(); + } + + uint32 Shield_Timer; + uint32 BloodLust_Timer; + uint32 GreaterHeal_Timer; + uint32 Disarm_Timer; + uint32 Check_Timer; + + bool FakeDeath; + + ScriptedInstance *pInstance; + + void Reset() + { + Shield_Timer = 1000; + BloodLust_Timer = 16000; + GreaterHeal_Timer = 32000; + Disarm_Timer = 6000; + Check_Timer = 10000; + + FakeDeath = false; + + if(pInstance) + pInstance->SetData(DATA_LORKHAN_ALIVE, 0); + + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit *who) + { + } + + void UpdateAI (const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //Shield_Timer + if(Shield_Timer < diff) + { + DoCast(m_creature,SPELL_SHIELD); + Shield_Timer = 61000; + }else Shield_Timer -= diff; + + //BloodLust_Timer + if(BloodLust_Timer < diff) + { + DoCast(m_creature,SPELL_BLOODLUST); + BloodLust_Timer = 20000+rand()%8000; + }else BloodLust_Timer -= diff; + + //Casting Greaterheal to Thekal or Zath if they are in meele range. + if(GreaterHeal_Timer < diff) + { + if(pInstance) + { + Unit *pThekal = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THEKAL)); + Unit *pZath = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_ZATH)); + + switch(rand()%2) + { + case 0: + if(m_creature->IsWithinDistInMap(pThekal, ATTACK_DISTANCE)) + DoCast(pThekal, SPELL_GREATERHEAL); + break; + case 1: + if(m_creature->IsWithinDistInMap(pZath, ATTACK_DISTANCE)) + DoCast(pZath, SPELL_GREATERHEAL); + break; + } + } + + GreaterHeal_Timer = 15000+rand()%5000; + }else GreaterHeal_Timer -= diff; + + //Disarm_Timer + if(Disarm_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_DISARM); + Disarm_Timer = 15000+rand()%10000; + }else Disarm_Timer -= diff; + + //Check_Timer for the death of LorKhan and Zath. + if(!FakeDeath && Check_Timer < diff) + { + if(pInstance) + { + if(pInstance->GetData(DATA_THEKALISFAKEDEAD)) + { + //Resurrect Thekal + Unit *pThekal = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THEKAL)); + pThekal->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + pThekal->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pThekal->setFaction(14); + pThekal->SetHealth(int(pThekal->GetMaxHealth()*1.0)); + } + + if(pInstance->GetData(DATA_ZATHISDEAD)) + { + //Resurrect Zath + Unit *pZath = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_ZATH)); + pZath->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + pZath->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pZath->setFaction(14); + pZath->SetHealth(int(pZath->GetMaxHealth()*1.0)); + } + } + + Check_Timer = 5000; + }else Check_Timer -= diff; + + if (m_creature->GetHealth() <= m_creature->GetMaxHealth() * 0.05) + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 3); + m_creature->setFaction(35); + m_creature->AttackStop(); + + if(pInstance) + pInstance->SetData(DATA_LORKHAN_DEATH, 0); + + FakeDeath = true; + } + + DoMeleeAttackIfReady(); + } +}; + +//Zealot Zath +struct MANGOS_DLL_DECL mob_zealot_zathAI : public ScriptedAI +{ + mob_zealot_zathAI(Creature *c) : ScriptedAI(c) + { + pInstance = (c->GetInstanceData()) ? ((ScriptedInstance*)c->GetInstanceData()) : NULL; + Reset(); + } + + uint32 SweepingStrikes_Timer; + uint32 SinisterStrike_Timer; + uint32 Gouge_Timer; + uint32 Kick_Timer; + uint32 Blind_Timer; + uint32 Check_Timer; + + bool FakeDeath; + + ScriptedInstance *pInstance; + + void Reset() + { + SweepingStrikes_Timer = 13000; + SinisterStrike_Timer = 8000; + Gouge_Timer = 25000; + Kick_Timer = 18000; + Blind_Timer = 5000; + Check_Timer = 10000; + + FakeDeath = false; + + if(pInstance) + pInstance->SetData(DATA_ZATH_ALIVE, 0); + + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void Aggro(Unit *who) + { + } + + void UpdateAI (const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim() ) + return; + + //SweepingStrikes_Timer + if(SweepingStrikes_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SWEEPINGSTRIKES); + SweepingStrikes_Timer = 22000+rand()%4000; + }else SweepingStrikes_Timer -= diff; + + //SinisterStrike_Timer + if(SinisterStrike_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_SINISTERSTRIKE); + SinisterStrike_Timer = 8000+rand()%8000; + }else SinisterStrike_Timer -= diff; + + //Gouge_Timer + if(Gouge_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_GOUGE); + + if(m_creature->getThreatManager().getThreat(m_creature->getVictim())) + m_creature->getThreatManager().modifyThreatPercent(m_creature->getVictim(),-100); + + Gouge_Timer = 17000+rand()%10000; + }else Gouge_Timer -= diff; + + //Kick_Timer + if(Kick_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_KICK); + Kick_Timer = 15000+rand()%10000; + }else Kick_Timer -= diff; + + //Blind_Timer + if(Blind_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_BLIND); + Blind_Timer = 10000+rand()%10000; + }else Blind_Timer -= diff; + + //Check_Timer for the death of LorKhan and Zath. + if(!FakeDeath && Check_Timer < diff) + { + if(pInstance) + { + if(pInstance->GetData(DATA_LORKHANISDEAD)) + { + //Resurrect LorKhan + Unit *pLorKhan = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_LORKHAN)); + pLorKhan->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + pLorKhan->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pLorKhan->setFaction(14); + pLorKhan->SetHealth(int(pLorKhan->GetMaxHealth()*1.0)); + } + + if(pInstance->GetData(DATA_THEKALISFAKEDEAD)) + { + //Resurrect Thekal + Unit *pThekal = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THEKAL)); + pThekal->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); + pThekal->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + pThekal->setFaction(14); + pThekal->SetHealth(int(pThekal->GetMaxHealth()*1.0)); + } + } + + Check_Timer = 5000; + }else Check_Timer -= diff; + + if (m_creature->GetHealth() <= m_creature->GetMaxHealth() * 0.05) + { + m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 3); + m_creature->setFaction(35); + m_creature->AttackStop(); + + if(pInstance) + pInstance->SetData(DATA_ZATH_DEATH, 0); + + FakeDeath = true; + } + + DoMeleeAttackIfReady(); + } +}; + +CreatureAI* GetAI_boss_thekal(Creature *_Creature) +{ + return new boss_thekalAI (_Creature); +} + +CreatureAI* GetAI_mob_zealot_lorkhan(Creature *_Creature) +{ + return new mob_zealot_lorkhanAI (_Creature); +} + +CreatureAI* GetAI_mob_zealot_zath(Creature *_Creature) +{ + return new mob_zealot_zathAI (_Creature); +} + +void AddSC_boss_thekal() +{ + Script *newscript; + + newscript = new Script; + newscript->Name="boss_thekal"; + newscript->GetAI = GetAI_boss_thekal; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_zealot_lorkhan"; + newscript->GetAI = GetAI_mob_zealot_lorkhan; + m_scripts[nrscripts++] = newscript; + + newscript = new Script; + newscript->Name="mob_zealot_zath"; + newscript->GetAI = GetAI_mob_zealot_zath; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_venoxis.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_venoxis.cpp new file mode 100644 index 00000000000..a2405ec9c6f --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_venoxis.cpp @@ -0,0 +1,200 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Venoxis +SD%Complete: 100 +SDComment: +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_HOLY_FIRE 23860 +#define SPELL_HOLY_WRATH 28883 //Not sure if this or 23979 +#define SPELL_VENOMSPIT 23862 +#define SPELL_HOLY_NOVA 23858 +#define SPELL_POISON_CLOUD 23861 +#define SPELL_SNAKE_FORM 23849 +#define SPELL_RENEW 23895 +#define SPELL_BERSERK 23537 +#define SPELL_DISPELL 23859 + +#define SAY_AGGRO "Let the coils of hate unfurl!" +#define SOUND_AGGRO 8421 + +struct MANGOS_DLL_DECL boss_venoxisAI : public ScriptedAI +{ + boss_venoxisAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 HolyFire_Timer; + uint32 HolyWrath_Timer; + uint32 VenomSpit_Timer; + uint32 Renew_Timer; + uint32 PoisonCloud_Timer; + uint32 HolyNova_Timer; + uint32 Dispell_Timer; + uint32 TargetInRange; + + bool PhaseTwo; + bool InBerserk; + + void Reset() + { + HolyFire_Timer = 10000; + HolyWrath_Timer = 60500; + VenomSpit_Timer = 5500; + Renew_Timer = 30500; + PoisonCloud_Timer = 2000; + HolyNova_Timer = 5000; + Dispell_Timer = 35000; + TargetInRange = 0; + + PhaseTwo = false; + InBerserk= false; + } + + void Aggro(Unit *who) + { + DoYell(SAY_AGGRO,LANG_UNIVERSAL,NULL); + DoPlaySoundToSet(m_creature,SOUND_AGGRO); + } + + void JustDied(Unit* Killer) + { + ScriptedInstance *pInstance = (m_creature->GetInstanceData()) ? ((ScriptedInstance*)m_creature->GetInstanceData()) : NULL; + if(pInstance) + pInstance->SetData(DATA_VENOXIS_DEATH, 0); + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget()) + return; + + if( m_creature->getVictim() && m_creature->isAlive()) + { + if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() > 50)) + { + if (Dispell_Timer < diff) + { + DoCast(m_creature, SPELL_DISPELL); + Dispell_Timer = 15000 + rand()%15000; + }else Dispell_Timer -= diff; + + if (Renew_Timer < diff) + { + DoCast(m_creature, SPELL_RENEW); + Renew_Timer = 20000 + rand()%10000; + }else Renew_Timer -= diff; + + if (HolyWrath_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_HOLY_WRATH); + HolyWrath_Timer = 15000 + rand()%10000; + }else HolyWrath_Timer -= diff; + + if (HolyNova_Timer < diff) + { + Unit* target = NULL; + TargetInRange = 0; + for(int i=0; i<10; i++) + { + target = SelectUnit(SELECT_TARGET_TOPAGGRO,i); + if(target) + if(m_creature->IsWithinDistInMap(target, ATTACK_DISTANCE)) + TargetInRange++; + } + + if(TargetInRange > 1) + { + DoCast(m_creature->getVictim(),SPELL_HOLY_NOVA); + HolyNova_Timer = 1000; + } + else + { + HolyNova_Timer = 2000; + } + + }else HolyNova_Timer -= diff; + + if (HolyFire_Timer < diff && TargetInRange < 3) + { + Unit* targetrandom = NULL; + targetrandom = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(targetrandom, SPELL_HOLY_FIRE); + HolyFire_Timer = 8000; + }else HolyFire_Timer -= diff; + } + else + { + if(!PhaseTwo) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature,SPELL_SNAKE_FORM); + m_creature->SetFloatValue(OBJECT_FIELD_SCALE_X, 2.00f); + const CreatureInfo *cinfo = m_creature->GetCreatureInfo(); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (cinfo->mindmg +((cinfo->mindmg/100) * 25))); + m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 25))); + m_creature->UpdateDamagePhysical(BASE_ATTACK); + DoResetThreat(); + PhaseTwo = true; + } + + if(PhaseTwo && PoisonCloud_Timer < diff) + { + DoCast(m_creature->getVictim(), SPELL_POISON_CLOUD); + PoisonCloud_Timer = 15000; + }PoisonCloud_Timer -=diff; + + if (PhaseTwo && VenomSpit_Timer < diff) + { + Unit* targetrandom = NULL; + targetrandom = SelectUnit(SELECT_TARGET_RANDOM,0); + + DoCast(targetrandom, SPELL_VENOMSPIT); + VenomSpit_Timer = 15000 + rand()%5000; + }else VenomSpit_Timer -= diff; + + if (PhaseTwo && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 11)) + { + if (!InBerserk) + { + m_creature->InterruptNonMeleeSpells(false); + DoCast(m_creature, SPELL_BERSERK); + InBerserk = true; + } + } + } + DoMeleeAttackIfReady(); + } + } +}; +CreatureAI* GetAI_boss_venoxis(Creature *_Creature) +{ + return new boss_venoxisAI (_Creature); +} + +void AddSC_boss_venoxis() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_venoxis"; + newscript->GetAI = GetAI_boss_venoxis; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/boss_wushoolay.cpp b/src/bindings/scripts/scripts/zone/zulgurub/boss_wushoolay.cpp new file mode 100644 index 00000000000..c94c665c1cb --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/boss_wushoolay.cpp @@ -0,0 +1,84 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Boss_Wushoolay +SD%Complete: 100 +SDComment: +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +#define SPELL_LIGHTNINGCLOUD 25033 +#define SPELL_LIGHTNINGWAVE 24819 + +struct MANGOS_DLL_DECL boss_wushoolayAI : public ScriptedAI +{ + boss_wushoolayAI(Creature *c) : ScriptedAI(c) {Reset();} + + uint32 LightningCloud_Timer; + uint32 LightningWave_Timer; + + void Reset() + { + LightningCloud_Timer = 5000 + rand()%5000; + LightningWave_Timer = 8000 + rand()%8000; + } + + void Aggro(Unit *who) + { + } + + void UpdateAI(const uint32 diff) + { + if (!m_creature->SelectHostilTarget() || !m_creature->getVictim()) + return; + + //LightningCloud_Timer + if (LightningCloud_Timer < diff) + { + DoCast(m_creature->getVictim(),SPELL_LIGHTNINGCLOUD); + LightningCloud_Timer = 15000 + rand()%5000; + }else LightningCloud_Timer -= diff; + + //LightningWave_Timer + if (LightningWave_Timer < diff) + { + Unit* target = NULL; + target = SelectUnit(SELECT_TARGET_RANDOM,0); + if (target) DoCast(target,SPELL_LIGHTNINGWAVE); + + LightningWave_Timer = 12000 + rand()%4000; + }else LightningWave_Timer -= diff; + + DoMeleeAttackIfReady(); + } +}; +CreatureAI* GetAI_boss_wushoolay(Creature *_Creature) +{ + return new boss_wushoolayAI (_Creature); +} + +void AddSC_boss_wushoolay() +{ + Script *newscript; + newscript = new Script; + newscript->Name="boss_wushoolay"; + newscript->GetAI = GetAI_boss_wushoolay; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/scripts/zone/zulgurub/def_zulgurub.h b/src/bindings/scripts/scripts/zone/zulgurub/def_zulgurub.h new file mode 100644 index 00000000000..0dcbb83c4df --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/def_zulgurub.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#ifndef DEF_ZULGURUB_H +#define DEF_ZULGURUB_H + +#define DATA_ARLOKKISDEAD 1 +#define DATA_ARLOKK_DEATH 2 +#define DATA_JEKLIKISDEAD 3 +#define DATA_JEKLIK_DEATH 4 +#define DATA_JINDO 5 +#define DATA_LORKHAN 6 +#define DATA_LORKHANISALIVE 7 +#define DATA_LORKHANISDEAD 8 +#define DATA_LORKHAN_ALIVE 9 +#define DATA_LORKHAN_DEATH 10 +#define DATA_MARLIISDEAD 11 +#define DATA_MARLI_DEATH 12 +#define DATA_OHGANISDEAD 13 +#define DATA_OHGAN_DEATH 14 +#define DATA_THEKAL 15 +#define DATA_THEKALFAKE_DEATH 16 +#define DATA_THEKALISALIVE 17 +#define DATA_THEKALISDEAD 18 +#define DATA_THEKALISFAKEDEAD 19 +#define DATA_THEKAL_ALIVE 20 +#define DATA_THEKAL_DEATH 21 +#define DATA_VENOXISISDEAD 22 +#define DATA_VENOXIS_DEATH 23 +#define DATA_ZATH 24 +#define DATA_ZATHISALIVE 25 +#define DATA_ZATHISDEAD 26 +#define DATA_ZATH_ALIVE 27 +#define DATA_ZATH_DEATH 28 +#endif diff --git a/src/bindings/scripts/scripts/zone/zulgurub/instance_zulgurub.cpp b/src/bindings/scripts/scripts/zone/zulgurub/instance_zulgurub.cpp new file mode 100644 index 00000000000..d0573414143 --- /dev/null +++ b/src/bindings/scripts/scripts/zone/zulgurub/instance_zulgurub.cpp @@ -0,0 +1,238 @@ +/* Copyright (C) 2006 - 2008 ScriptDev2 + * 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 + */ + +/* ScriptData +SDName: Instance_ZulGurub +SD%Complete: 80 +SDComment: Missing reset function after killing a boss for Ohgan, Thekal. +SDCategory: Zul'Gurub +EndScriptData */ + +#include "precompiled.h" +#include "def_zulgurub.h" + +struct MANGOS_DLL_DECL instance_zulgurub : public ScriptedInstance +{ + instance_zulgurub(Map *Map) : ScriptedInstance(Map) {Initialize();}; + + //If all High Priest bosses were killed. Lorkhan, Zath and Ohgan are added too. + bool IsBossDied[9]; + + //Storing Lorkhan, Zath and Thekal because we need to cast on them later. Jindo is needed for healfunction too. + uint64 LorKhanGUID; + uint64 ZathGUID; + uint64 ThekalGUID; + uint64 JindoGUID; + + void OnCreatureCreate (Creature *creature, uint32 creature_entry) + { + switch (creature_entry) + { + case 11347: + LorKhanGUID = creature->GetGUID(); + break; + + case 11348: + ZathGUID = creature->GetGUID(); + break; + + case 14509: + ThekalGUID = creature->GetGUID(); + break; + + case 11380: + JindoGUID = creature->GetGUID(); + break; + } + } + + void Initialize() + { + IsBossDied[0] = false; + IsBossDied[1] = false; + IsBossDied[2] = false; + IsBossDied[3] = false; + IsBossDied[4] = false; + IsBossDied[5] = false; + IsBossDied[6] = false; + + IsBossDied[7] = false; + IsBossDied[8] = false; + } + + bool IsEncounterInProgress() const + { + //not active in Zul'Gurub + return false; + } + + uint32 GetData(uint32 type) + { + switch(type) + { + case DATA_JEKLIKISDEAD: + if(IsBossDied[0]) + return 1; + break; + + case DATA_VENOXISISDEAD: + if(IsBossDied[1]) + return 1; + break; + + case DATA_MARLIISDEAD: + if(IsBossDied[2]) + return 1; + break; + + case DATA_THEKALISDEAD: + if(IsBossDied[3]) + return 1; + break; + + case DATA_ARLOKKISDEAD: + if(IsBossDied[4]) + return 1; + break; + + case DATA_LORKHANISDEAD: + if(IsBossDied[5]) + return 1; + break; + + case DATA_ZATHISDEAD: + if(IsBossDied[6]) + return 1; + break; + + case DATA_THEKALISFAKEDEAD: + if(IsBossDied[7]) + return 1; + break; + + case DATA_OHGANISDEAD: + if(IsBossDied[8]) + return 1; + break; + + //Boss is not dead. Resetting function for some bosses after killing them but whiping at the complete encounter. + + case DATA_THEKALISALIVE: + if(IsBossDied[3]) + return 0; + break; + + case DATA_LORKHANISALIVE: + if(IsBossDied[5]) + return 0; + break; + + case DATA_ZATHISALIVE: + if(IsBossDied[6]) + return 0; + break; + } + + return 0; + } + + uint64 GetData64 (uint32 identifier) + { + switch(identifier) + { + case DATA_LORKHAN: + return LorKhanGUID; + + case DATA_ZATH: + return ZathGUID; + + case DATA_THEKAL: + return ThekalGUID; + + case DATA_JINDO: + return JindoGUID; + } + return 0; + } // end GetData64 + + void SetData(uint32 type, uint32 data) + { + switch(type) + { + case DATA_JEKLIK_DEATH: + IsBossDied[0] = true; + break; + + case DATA_VENOXIS_DEATH: + IsBossDied[1] = true; + break; + + case DATA_MARLI_DEATH: + IsBossDied[2] = true; + break; + + case DATA_THEKAL_DEATH: + IsBossDied[3] = true; + break; + + case DATA_ARLOKK_DEATH: + IsBossDied[4] = true; + break; + + case DATA_LORKHAN_DEATH: + IsBossDied[5] = true; + break; + + case DATA_ZATH_DEATH: + IsBossDied[6] = true; + break; + + case DATA_THEKALFAKE_DEATH: + IsBossDied[7] = true; + break; + + case DATA_OHGAN_DEATH: + IsBossDied[8] = true; + break; + + case DATA_LORKHAN_ALIVE: + IsBossDied[5] = false; + break; + + case DATA_ZATH_ALIVE: + IsBossDied[6] = false; + break; + + case DATA_THEKAL_ALIVE: + IsBossDied[7] = false; + break; + } + } +}; + +InstanceData* GetInstanceData_instance_zulgurub(Map* map) +{ + return new instance_zulgurub(map); +} + +void AddSC_instance_zulgurub() +{ + Script *newscript; + newscript = new Script; + newscript->Name = "instance_zulgurub"; + newscript->GetInstanceData = GetInstanceData_instance_zulgurub; + m_scripts[nrscripts++] = newscript; +} diff --git a/src/bindings/scripts/sql/Makefile.am b/src/bindings/scripts/sql/Makefile.am new file mode 100644 index 00000000000..27d64eb6f6e --- /dev/null +++ b/src/bindings/scripts/sql/Makefile.am @@ -0,0 +1,31 @@ +# Copyright (C) 2005,2006,2007 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse +SUBDIRS = Updates + +## Change installation location +# datadir = scriptdev2/sql +pkgdatadir = $(datadir)/scriptdev2/sql + +## Files to be installed +# Install basic SQL files to datadir +pkgdata_DATA = \ + create_database.sql \ + mangos_full_scripts.sql \ + scriptdev2_structure.sql diff --git a/src/bindings/scripts/sql/Makefile.in b/src/bindings/scripts/sql/Makefile.in new file mode 100644 index 00000000000..a07a30e0826 --- /dev/null +++ b/src/bindings/scripts/sql/Makefile.in @@ -0,0 +1,540 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Copyright (C) 2005,2006,2007 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../../../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/bindings/ScriptDev2/sql +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ + install-exec-recursive install-info-recursive \ + install-recursive installcheck-recursive installdirs-recursive \ + pdf-recursive ps-recursive uninstall-info-recursive \ + uninstall-recursive +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(pkgdatadir)" +pkgdataDATA_INSTALL = $(INSTALL_DATA) +DATA = $(pkgdata_DATA) +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +# datadir = scriptdev2/sql +pkgdatadir = $(datadir)/scriptdev2/sql +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +COMPATLIB = @COMPATLIB@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DOXYGENPATH = @DOXYGENPATH@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +MANGOSD_DEBUG_INFO = @MANGOSD_DEBUG_INFO@ +MANGOSD_ENABLE_CLI = @MANGOSD_ENABLE_CLI@ +MANGOSD_ENABLE_RA = @MANGOSD_ENABLE_RA@ +MYSQL_CONFIG = @MYSQL_CONFIG@ +MYSQL_INCLUDES = @MYSQL_INCLUDES@ +MYSQL_LIBS = @MYSQL_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSLLIB = @SSLLIB@ +STRIP = @STRIP@ +USE_DOXYGEN_FALSE = @USE_DOXYGEN_FALSE@ +USE_DOXYGEN_TRUE = @USE_DOXYGEN_TRUE@ +VERSION = @VERSION@ +ZLIB = @ZLIB@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +SUBDIRS = Updates + +# Install basic SQL files to datadir +pkgdata_DATA = \ + scripts.sql + +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/bindings/ScriptDev2/sql/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/bindings/ScriptDev2/sql/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-pkgdataDATA: $(pkgdata_DATA) + @$(NORMAL_INSTALL) + test -z "$(pkgdatadir)" || $(mkdir_p) "$(DESTDIR)$(pkgdatadir)" + @list='$(pkgdata_DATA)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(pkgdataDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(pkgdatadir)/$$f'"; \ + $(pkgdataDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(pkgdatadir)/$$f"; \ + done + +uninstall-pkgdataDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgdata_DATA)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(pkgdatadir)/$$f'"; \ + rm -f "$(DESTDIR)$(pkgdatadir)/$$f"; \ + done + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ + *k*) failcom='fail=yes';; \ + esac; \ + done; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done +ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ + || $(mkdir_p) "$(distdir)/$$subdir" \ + || exit 1; \ + distdir=`$(am__cd) $(distdir) && pwd`; \ + top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$top_distdir" \ + distdir="$$distdir/$$subdir" \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(DATA) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(pkgdatadir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +info: info-recursive + +info-am: + +install-data-am: install-pkgdataDATA + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-info-am uninstall-pkgdataDATA + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am check check-am \ + clean clean-generic clean-libtool clean-recursive ctags \ + ctags-recursive distclean distclean-generic distclean-libtool \ + distclean-recursive distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-exec install-exec-am install-info \ + install-info-am install-man install-pkgdataDATA install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-recursive mostlyclean mostlyclean-generic \ + mostlyclean-libtool mostlyclean-recursive pdf pdf-am ps ps-am \ + tags tags-recursive uninstall uninstall-am uninstall-info-am \ + uninstall-pkgdataDATA + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/bindings/scripts/sql/Updates/09_BraveWindfeather.sql b/src/bindings/scripts/sql/Updates/09_BraveWindfeather.sql new file mode 100644 index 00000000000..ae78c707323 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/09_BraveWindfeather.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET`ScriptName` = '' WHERE `entry` = 3209; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/11_SilvaFilnaveth.sql b/src/bindings/scripts/sql/Updates/11_SilvaFilnaveth.sql new file mode 100644 index 00000000000..9900466b717 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/11_SilvaFilnaveth.sql @@ -0,0 +1 @@ +UPDATE `creature` SET `id` = 11798, `curhealth` = 3857 WHERE `guid` = 46833; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/27_Vaelastraz.sql b/src/bindings/scripts/sql/Updates/27_Vaelastraz.sql new file mode 100644 index 00000000000..e57ed2107d9 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/27_Vaelastraz.sql @@ -0,0 +1,2 @@ +UPDATE creature_template SET faction=35 WHERE entry=13020; +UPDATE creature_template SET npcflag=65 WHERE entry=13020; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/Makefile.am b/src/bindings/scripts/sql/Updates/Makefile.am new file mode 100644 index 00000000000..e8389337a6a --- /dev/null +++ b/src/bindings/scripts/sql/Updates/Makefile.am @@ -0,0 +1,215 @@ +# Copyright (C) 2005,2006,2007 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## Change installation location +# datadir = scriptdev2/sql/updates +pkgdatadir = $(datadir)/scriptdev2/sql/updates + +## Files to be installed +# Install basic SQL files to datadir +pkgdata_DATA = \ + 09_BraveWindfeather.sql \ + 11_SilvaFilnaveth.sql \ + 27_Vaelastraz.sql \ + r56.sql \ + r59.sql \ + r63.sql \ + r65.sql \ + r72.sql \ + r78.sql \ + r81.sql \ + r91.sql \ + r92.sql \ + r97.sql \ + r108.sql \ + r110.sql \ + r121.sql \ + r123.sql \ + r124.sql \ + r125.sql \ + r128.sql \ + r131.sql \ + r134.sql \ + r136.sql \ + r139.sql \ + r142.sql \ + r144.sql \ + r145.sql \ + r149.sql \ + r150.sql \ + r152.sql \ + r153.sql \ + r157.sql \ + r161.sql \ + r163.sql \ + r169.sql \ + r170.sql \ + r171.sql \ + r172.sql \ + r174.sql \ + r176.sql \ + r177.sql \ + r178.sql \ + r181.sql \ + r182.sql \ + r183.sql \ + r184.sql \ + r186.sql \ + r187.sql \ + r189.sql \ + r191.sql \ + r192.sql \ + r197.sql \ + r204.sql \ + r206.sql \ + r211.sql \ + r213.sql \ + r214.sql \ + r215.sql \ + r218.sql \ + r219.sql \ + r221_scriptdev2.sql \ + r227_scriptdev2.sql \ + r230_mangos.sql \ + r234_mangos.sql \ + r237_mangos.sql \ + r238_mangos.sql \ + r239_mangos.sql \ + r240_scriptdev2.sql \ + r241_mangos.sql \ + r242_mangos.sql \ + r243_mangos.sql \ + r249_mangos.sql \ + r250_mangos.sql \ + r253_mangos.sql \ + r255_mangos.sql \ + r256_mangos.sql \ + r257_mangos.sql \ + r258_mangos.sql \ + r260_mangos.sql \ + r261_mangos.sql \ + r262_mangos.sql \ + r263_mangos.sql \ + r264_mangos.sql \ + r265_mangos.sql \ + r269_mangos.sql \ + r270_mangos.sql \ + r271_mangos.sql \ + r272_mangos.sql \ + r273_mangos.sql \ + r274_mangos.sql \ + r275_mangos.sql \ + r281_scriptdev2.sql \ + r282_mangos.sql \ + r286_mangos.sql \ + r289_mangos.sql \ + r291_mangos.sql \ + r295_mangos.sql \ + r297_mangos.sql \ + r298_scriptdev2.sql \ + r299_scriptdev2.sql \ + r304_mangos.sql \ + r306_scriptdev2.sql \ + r307_mangos.sql \ + r308_mangos.sql \ + r309_mangos.sql \ + r311_mangos.sql \ + r312_mangos.sql \ + r318_mangos.sql \ + r324_mangos.sql \ + r327_mangos.sql \ + r332_scriptdev2.sql \ + r333_mangos.sql \ + r334_mangos.sql \ + r336_mangos.sql \ + r352_mangos.sql \ + r355_mangos.sql \ + r358_mangos.sql \ + r364_mangos.sql \ + r367_mangos.sql \ + r368_mangos.sql \ + r369_mangos.sql \ + r374_mangos.sql \ + r386_mangos.sql \ + r417_mangos.sql \ + r428_mangos.sql \ + r431_mangos.sql \ + r444_mangos.sql \ + r445_mangos.sql \ + r446_mangos.sql \ + r448_scriptdev2.sql \ + r462_mangos.sql \ + r465_mangos.sql \ + r467_mangos.sql \ + r473_mangos.sql \ + r476_mangos.sql \ + r477_mangos.sql \ + r479_mangos.sql \ + r482_mangos.sql \ + r484_mangos.sql \ + r486_mangos.sql \ + r487_mangos.sql \ + r494_mangos.sql \ + r501_mangos.sql \ + r513_mangos.sql \ + r514_mangos.sql \ + r515_mangos.sql \ + r516_mangos.sql \ + r517_mangos.sql \ + r518_mangos.sql \ + r519_mangos.sql \ + r520_mangos.sql \ + r521_mangos.sql \ + r522_mangos.sql \ + r526_mangos.sql \ + r528_mangos.sql \ + r533_mangos.sql \ + r538_scriptdev2.sql \ + r547_mangos.sql \ + r554_mangos.sql \ + r555_mangos.sql \ + r575_mangos.sql \ + r576_mangos.sql \ + r578_mangos.sql \ + r584_mangos.sql \ + r590_mangos.sql \ + r591_mangos.sql \ + r593_mangos.sql \ + r594_mangos.sql \ + r596_mangos.sql \ + r610_mangos.sql \ + r615_scriptdev2.sql \ + r617_mangos.sql \ + r621_mangos.sql \ + r628_mangos.sql \ + r632_mangos.sql \ + r633_mangos.sql \ + r634_scriptdev2.sql \ + r636_mangos.sql \ + r637_mangos.sql \ + r638_mangos.sql \ + r639_mangos.sql \ + r642_mangos.sql \ + r643_mangos.sql \ + r646_mangos.sql \ + r647_scriptdev2.sql \ + r656_scriptdev2.sql \ + r658_mangos.sql \ + r659_mangos.sql diff --git a/src/bindings/scripts/sql/Updates/Makefile.in b/src/bindings/scripts/sql/Updates/Makefile.in new file mode 100644 index 00000000000..9b43048f032 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/Makefile.in @@ -0,0 +1,383 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Copyright (C) 2005,2006,2007 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../../../../.. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/bindings/ScriptDev2/sql/Updates +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +SOURCES = +DIST_SOURCES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = `echo $$p | sed -e 's|^.*/||'`; +am__installdirs = "$(DESTDIR)$(pkgdatadir)" +pkgdataDATA_INSTALL = $(INSTALL_DATA) +DATA = $(pkgdata_DATA) +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +# datadir = scriptdev2/sql/updates +pkgdatadir = $(datadir)/scriptdev2/sql/updates +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +COMPATLIB = @COMPATLIB@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DOXYGENPATH = @DOXYGENPATH@ +ECHO = @ECHO@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +F77 = @F77@ +FFLAGS = @FFLAGS@ +GREP = @GREP@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ +MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ +MAKEINFO = @MAKEINFO@ +MANGOSD_DEBUG_INFO = @MANGOSD_DEBUG_INFO@ +MANGOSD_ENABLE_CLI = @MANGOSD_ENABLE_CLI@ +MANGOSD_ENABLE_RA = @MANGOSD_ENABLE_RA@ +MYSQL_CONFIG = @MYSQL_CONFIG@ +MYSQL_INCLUDES = @MYSQL_INCLUDES@ +MYSQL_LIBS = @MYSQL_LIBS@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSLLIB = @SSLLIB@ +STRIP = @STRIP@ +USE_DOXYGEN_FALSE = @USE_DOXYGEN_FALSE@ +USE_DOXYGEN_TRUE = @USE_DOXYGEN_TRUE@ +VERSION = @VERSION@ +ZLIB = @ZLIB@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_F77 = @ac_ct_F77@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ + +# Install basic SQL files to datadir +pkgdata_DATA = \ + 09_BraveWindfeather.sql \ + 11_SilvaFilnaveth.sql \ + 27_Vaelastraz.sql + +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/bindings/ScriptDev2/sql/Updates/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/bindings/ScriptDev2/sql/Updates/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +install-pkgdataDATA: $(pkgdata_DATA) + @$(NORMAL_INSTALL) + test -z "$(pkgdatadir)" || $(mkdir_p) "$(DESTDIR)$(pkgdatadir)" + @list='$(pkgdata_DATA)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f=$(am__strip_dir) \ + echo " $(pkgdataDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(pkgdatadir)/$$f'"; \ + $(pkgdataDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(pkgdatadir)/$$f"; \ + done + +uninstall-pkgdataDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgdata_DATA)'; for p in $$list; do \ + f=$(am__strip_dir) \ + echo " rm -f '$(DESTDIR)$(pkgdatadir)/$$f'"; \ + rm -f "$(DESTDIR)$(pkgdatadir)/$$f"; \ + done +tags: TAGS +TAGS: + +ctags: CTAGS +CTAGS: + + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(DATA) +installdirs: + for dir in "$(DESTDIR)$(pkgdatadir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-libtool + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-pkgdataDATA + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-info-am uninstall-pkgdataDATA + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + distclean distclean-generic distclean-libtool distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-pkgdataDATA \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + uninstall uninstall-am uninstall-info-am uninstall-pkgdataDATA + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/bindings/scripts/sql/Updates/r104.sql b/src/bindings/scripts/sql/Updates/r104.sql new file mode 100644 index 00000000000..29372b726b7 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r104.sql @@ -0,0 +1 @@ +UPDATE `gameobject_template` SET `ScriptName`='go_orb_of_command' WHERE `entry`='179879'; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r108.sql b/src/bindings/scripts/sql/Updates/r108.sql new file mode 100644 index 00000000000..7c50fe13d77 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r108.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'mob_dementeddruids' WHERE `entry` = 15260; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r110.sql b/src/bindings/scripts/sql/Updates/r110.sql new file mode 100644 index 00000000000..d5bd73a2fa2 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r110.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'mob_illusionofjandicebarov' WHERE `entry` = 11439; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r121.sql b/src/bindings/scripts/sql/Updates/r121.sql new file mode 100644 index 00000000000..597bf532508 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r121.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName` = 'mobs_spectral_ghostly_citizen' WHERE `entry` IN (10384, 10385); + diff --git a/src/bindings/scripts/sql/Updates/r123.sql b/src/bindings/scripts/sql/Updates/r123.sql new file mode 100644 index 00000000000..14f73d1e2d3 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r123.sql @@ -0,0 +1,4 @@ +UPDATE creature_template SET ScriptName = 'dashel_stonefist' WHERE entry = '4961'; +UPDATE creature_template SET ScriptName = 'blood_knight_stillblade' WHERE entry = '17768'; +UPDATE creature_template SET ScriptName = 'shattered_rumbler' WHERE entry = '17157'; +UPDATE `creature_template` SET `ScriptName`='neltharaku' WHERE `entry`='21657'; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r124.sql b/src/bindings/scripts/sql/Updates/r124.sql new file mode 100644 index 00000000000..4b5f5cb5794 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r124.sql @@ -0,0 +1,13 @@ +-- BT Trash Mobs +UPDATE `creature_template` SET `scriptname`='mob_blacktemple' WHERE `entry` IN (22844,22845,22846,22849,22853,22855,22869,22873,22875,22876,22877,22878,22880,22881,22882,22883,22884,22945,22953,22954,23337,23339); + +-- The Eye Trash Mobs +UPDATE `creature_template` SET `scriptname`='mob_the_eye' WHERE `entry` IN (20031,20032,20033,20034,20035,20039,20041,20043,20046,20052); +UPDATE `creature_template` SET `scriptname`='mob_crystalcore_devastator' WHERE `entry`='20040'; + +-- Uldaman Trash Mobs +UPDATE `creature_template` SET `scriptname`='mob_uldaman' WHERE `entry` IN (4847,4852,4853,4854,4860,4861,6910,7011,7012,7022,7030,7078,7291); +UPDATE `creature_template` SET `scriptname`='mob_jadespine_basilisk' WHERE `entry`='4863'; + +-- Uldaman Boss Ironaya +UPDATE `creature_template` SET `scriptname`='boss_ironaya' WHERE `entry`='7228'; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r125.sql b/src/bindings/scripts/sql/Updates/r125.sql new file mode 100644 index 00000000000..85477c24e08 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r125.sql @@ -0,0 +1,24 @@ +UPDATE `creature_template` SET `ScriptName` = 'mob_infested_root_walker' WHERE `entry` = 22095; +UPDATE `creature_template` SET `ScriptName` = 'mob_rotting_forest_rager' WHERE `entry` = 22307; +UPDATE `creature_template` SET `ScriptName` = 'mob_netherweb_victim' WHERE `entry` = 22355; + +UPDATE `creature_template` SET `ScriptName` = 'mobs_shadow_council_covert' WHERE `entry` IN (18716, 18717, 18719); +UPDATE `creature_template` SET `npcflag` = 1 WHERE `entry` IN (18716, 18717, 18719); + +UPDATE `creature_template` SET `ScriptName` = 'mob_gurok_the_usurper' WHERE `entry` = 18182; +UPDATE `creature_template` SET `ScriptName` = 'mob_shattered_rumbler' WHERE `entry` = '17157'; +UPDATE `creature_template` SET `ScriptName` = 'mobs_kilsorrow_agent' WHERE `entry` IN (17146, 17147, 17148, 18397, 18658); +UPDATE `creature_template` SET `ScriptName` = 'npc_altruis_the_sufferer' WHERE `entry` = 18417; + +UPDATE `creature_template` SET `ScriptName` = 'npcs_ashyen_and_keleth' WHERE `entry` IN (17900, 17901); +UPDATE `creature_template` SET `npcflag` = 1 WHERE `entry` IN (17900,17901); +UPDATE `creature_template` SET `ScriptName` = 'npc_elder_kuruti' WHERE `entry` = 18197; +UPDATE `creature_template` SET `npcflag` = 1 WHERE `entry` = 18197; + +UPDATE `creature_template` SET `ScriptName` = 'mob_lunaclaw' WHERE `entry` = 12138; + +UPDATE `creature_template` SET `ScriptName` = 'npc_steward_of_time' WHERE `entry` = 20142; + +UPDATE `creature_template` SET `ScriptName` = 'boss_death_knight_darkreaver' WHERE `entry` = 14516; + +UPDATE `creature_template` SET `ScriptName` = 'mob_yenniku' WHERE `entry` = 2530; diff --git a/src/bindings/scripts/sql/Updates/r128.sql b/src/bindings/scripts/sql/Updates/r128.sql new file mode 100644 index 00000000000..f652fa16f10 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r128.sql @@ -0,0 +1 @@ +update `creature_template` SET `scriptname`='npc_guardian' WHERE `entry`='5764'; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r131.sql b/src/bindings/scripts/sql/Updates/r131.sql new file mode 100644 index 00000000000..0899dae38bc --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r131.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_henze_faulk' WHERE `entry`= 6172; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r134.sql b/src/bindings/scripts/sql/Updates/r134.sql new file mode 100644 index 00000000000..eac5bb78415 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r134.sql @@ -0,0 +1,15 @@ + +UPDATE `instance_template` SET `script` = 'instance_molten_core' WHERE `map`= 409; +UPDATE `instance_template` SET `script` = 'instance_blackwing_lair' WHERE `map`= 469; +UPDATE `instance_template` SET `script` = 'instance_zulgurub' WHERE `map`= 309; +UPDATE `instance_template` SET `script` = 'instance_ruins_of_ahnqiraj' WHERE `map`= 509; +UPDATE `instance_template` SET `script` = 'instance_temple_of_ahnqiraj' WHERE `map`= 531; +UPDATE `instance_template` SET `script` = 'instance_naxxramas' WHERE `map`= 533; + +UPDATE `instance_template` SET `script` = 'instance_karazhan' WHERE `map`= 532; +UPDATE `instance_template` SET `script` = 'instance_hyjal' WHERE `map`= 534; +UPDATE `instance_template` SET `script` = 'instance_gruuls_lair' WHERE `map`= 565; +UPDATE `instance_template` SET `script` = 'instance_magtheridons_lair' WHERE `map`= 544; +UPDATE `instance_template` SET `script` = 'instance_serpent_shrine' WHERE `map`= 548; +UPDATE `instance_template` SET `script` = 'instance_the_eye' WHERE `map`= 550; +UPDATE `instance_template` SET `script` = 'instance_black_temple' WHERE `map`= 564; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r136.sql b/src/bindings/scripts/sql/Updates/r136.sql new file mode 100644 index 00000000000..52a9d87d6de --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r136.sql @@ -0,0 +1,55 @@ + +-- AQ40 bosses -- + +-- Skeram 15263 +UPDATE `creature_template` SET `ScriptName` = 'boss_skeram' WHERE `entry` = 15263; + +-- Vem, Yauj, Kri +-- Vem 15544 +UPDATE `creature_template` SET `ScriptName` = 'boss_vem' WHERE `entry` = 15544; +-- Yauj 15543 +UPDATE `creature_template` SET `ScriptName` = 'boss_yauj' WHERE `entry` = 15543; +-- Yauj Brood 15621 +UPDATE `creature_template` SET `ScriptName` = 'mob_yauj_brood' WHERE `entry` = 15621; +-- Kri 15511 +UPDATE `creature_template` SET `ScriptName` = 'boss_kri' WHERE `entry` = 15511; + +-- Sartura 15516 +UPDATE `creature_template` SET `ScriptName` = 'boss_sartura' WHERE `entry` = 15516; +-- Sartura's Royal Guard 15984 +UPDATE `creature_template` SET `ScriptName` = 'mob_sartura_royal_guard' WHERE `entry` = 15984; + +-- Fankriss 15510 +UPDATE `creature_template` SET `ScriptName` = 'boss_fankriss' WHERE `entry` = 15510; +-- Spawn of Fankriss 15630 +UPDATE `creature_template` SET `ScriptName` = 'mob_spawn_of_fankriss' WHERE `entry` = 15630; + +-- Viscidus 15299 +UPDATE `creature_template` SET `ScriptName` = 'boss_viscidus' WHERE `entry` = 15299; +-- Glob of Viscidus 15667 +UPDATE `creature_template` SET `ScriptName` = 'boss_glob_of_viscidus' WHERE `entry` = 15667; + +-- Huhuran 15509 +UPDATE `creature_template` SET `ScriptName` = 'boss_huhuran' WHERE `entry` = 15509; + +-- Veklor & Veknilash +-- Veklor 15276 +UPDATE `creature_template` SET `ScriptName` = 'boss_veklor' WHERE `entry` = 15276; +-- Veknilash 15275 +UPDATE `creature_template` SET `ScriptName` = 'boss_veknilash' WHERE `entry` = 15275; + +-- Ouro 15517 +UPDATE `creature_template` SET `ScriptName` = 'boss_ouro' WHERE `entry` = 15517; + +-- C'thun +-- Eye 15589 +-- Body 15727 + +UPDATE creature_template SET ScriptName='boss_eye_of_cthun' WHERE entry=15589; +UPDATE creature_template SET ScriptName='mob_claw_tentacle' WHERE entry=15725; +UPDATE creature_template SET ScriptName='mob_eye_tentacle' WHERE entry=15726; + +UPDATE creature_template SET ScriptName='boss_cthun' WHERE entry=15727; +UPDATE creature_template SET ScriptName='mob_giant_claw_tentacle' WHERE entry=15728; +UPDATE creature_template SET ScriptName='mob_giant_eye_tentacle' WHERE entry=15334; +UPDATE creature_template SET ScriptName='mob_giant_flesh_tentacle' WHERE entry=15802; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r139.sql b/src/bindings/scripts/sql/Updates/r139.sql new file mode 100644 index 00000000000..73c013f1a1f --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r139.sql @@ -0,0 +1,35 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_lokhtos_darkbargainer' WHERE `entry` = 12944; + +UPDATE creature_template SET ScriptName='npc_lady_jaina_proudmoore' WHERE entry=4968; + +UPDATE `creature_template` SET `ScriptName` = 'mobs_spitelashes' WHERE entry IN (6190,6193,6194,6195,6196,7885,7886,12204,12205); + +UPDATE creature_template SET npcflag=1, ScriptName='npc_beaten_corpse' WHERE entry=10668; +UPDATE quest_template SET SpecialFlags=8, ReqCreatureOrGOId1=10668, ReqCreatureOrGOCount1=1 WHERE entry=4921; + +UPDATE creature_template SET ScriptName='boss_rokmar_the_crackler' WHERE entry=17991; +UPDATE creature_template SET ScriptName='boss_warlord_kalithresh' WHERE entry=17798; + +UPDATE `creature_template` SET `ScriptName` = 'boss_midnight' WHERE `entry` = 16151; +UPDATE `creature_template` SET `ScriptName` = 'boss_attumen' WHERE `entry` = 15550; +UPDATE `creature_template` SET `ScriptName` = 'boss_moroes' WHERE `entry` = 15687; +UPDATE `creature_template` SET `ScriptName` = 'boss_maiden_of_virtue' WHERE `entry` = 16457; +UPDATE `creature_template` SET `ScriptName` = 'boss_curator' WHERE `entry` = 15691; +UPDATE `creature_template` SET `ScriptName` = 'boss_big_bad_wolf' WHERE `entry` = 17521; +UPDATE `creature_template` SET `ScriptName` = 'boss_julianne' WHERE `entry` = 17534; +UPDATE `creature_template` SET `ScriptName` = 'boss_romulo' WHERE `entry` = 17533; +UPDATE `creature_template` SET `ScriptName` = 'boss_dorothee' WHERE `entry` = 17535; +UPDATE `creature_template` SET `ScriptName` = 'boss_strawman' WHERE `entry` = 17543; +UPDATE `creature_template` SET `ScriptName` = 'boss_tinhead' WHERE `entry` = 17547; +UPDATE `creature_template` SET `ScriptName` = 'boss_tito' WHERE `entry` = 17548; +UPDATE `creature_template` SET `ScriptName` = 'boss_roar' WHERE `entry` = 17546; +UPDATE `creature_template` SET `ScriptName` = 'boss_crone' WHERE `entry` = 18168; +UPDATE `creature_template` SET `ScriptName` = 'boss_terestian_illhoof' WHERE `entry` = 15688; +UPDATE `creature_template` SET `ScriptName` = 'boss_shade_of_aran' WHERE `entry` = 16524; +UPDATE `creature_template` SET `ScriptName` = 'boss_netherspite' WHERE `entry` = 15689; +UPDATE `creature_template` SET `ScriptName` = 'boss_malchezaar' WHERE `entry` = 15690; +UPDATE `creature_template` SET `ScriptName` = 'boss_nightbane' WHERE `entry` = 17225; + +UPDATE `creature_template` SET `ScriptName` = 'mob_homunculus' WHERE `entry` = 16539; +UPDATE `creature_template` SET `ScriptName` = 'mob_kilrek' WHERE `entry` = 17229; +UPDATE `creature_template` SET `ScriptName` = 'netherspite_infernal' WHERE `entry` = 17646; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r142.sql b/src/bindings/scripts/sql/Updates/r142.sql new file mode 100644 index 00000000000..2cd9b29e6ec --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r142.sql @@ -0,0 +1,11 @@ +-- VoidReaver 19516 +UPDATE `creature_template` SET `ScriptName` = 'boss_void_reaver' WHERE `entry` = 19516; + +-- Arcane Orb Target +REPLACE INTO `creature_template` (`entry`, `modelid_A`, `modelid_H`, `name`, `subname`, `minlevel`, `maxlevel`, `minhealth`, `maxhealth`, `minmana`, `maxmana`, `armor`, `faction_A`, `faction_H`, `npcflag`, `speed`, `rank`, `mindmg`, `maxdmg`, `dmgschool`, `attackpower`, `baseattacktime`, `rangeattacktime`, `flags`, `dynamicflags`, `family`, `trainer_type`, `trainer_spell`, `class`, `race`, `minrangedmg`, `maxrangedmg`, `rangedattackpower`, `type`, `civilian`, `flag1`, `lootid`, `pickpocketloot`, `skinloot`, `resistance1`, `resistance2`, `resistance3`, `resistance4`, `resistance5`, `resistance6`, `spell1`, `spell2`, `spell3`, `spell4`, `mingold`, `maxgold`, `AIName`, `MovementType`, `InhabitType`, `RacialLeader`, `RegenHealth`, `equipment_id`, `ScriptName`) VALUES (19577, 16946, 16946, 'Arcane Orb Target', '', 70, 70, 1, 1, 0, 0, 0, 35, 35, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 3, 0, 1, 0, 'mob_arcane_orb_target'); + +UPDATE `creature_template` SET `ScriptName` = 'boss_omor_the_unscarred' WHERE `entry` = 17308; +UPDATE `creature_template` SET `ScriptName` = 'boss_watchkeeper_gargolmar' WHERE `entry` = 17306; + +UPDATE `creature_template` SET `ScriptName` = 'boss_hungarfen' WHERE `entry` = 17770; +UPDATE `creature_template` SET `ScriptName` = 'mob_underbog_mushroom' WHERE `entry` = 17990; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r144.sql b/src/bindings/scripts/sql/Updates/r144.sql new file mode 100644 index 00000000000..ddf354b9ea1 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r144.sql @@ -0,0 +1,5 @@ +UPDATE `creature_template` SET `ScriptName` = 'boss_the_maker' WHERE `entry` = 17381; +UPDATE `creature_template` SET `ScriptName` = 'boss_broggok' WHERE `entry` = 17380; +UPDATE `creature_template` SET `ScriptName` = 'boss_kelidan_the_breaker' WHERE `entry` = 17377; +UPDATE `creature_template` SET `ScriptName` = 'mob_broggok_poisoncloud' WHERE `entry` = 17662; +UPDATE `creature_template` SET minlevel = 63, maxlevel = 63 WHERE `entry` = 17662; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r145.sql b/src/bindings/scripts/sql/Updates/r145.sql new file mode 100644 index 00000000000..d2d79974048 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r145.sql @@ -0,0 +1,4 @@ + +UPDATE `creature_template` SET `ScriptName` = 'mob_shadow_of_aran' WHERE `entry` = 18254; +UPDATE `creature_template` SET `ScriptName` = 'mob_aran_elemental' WHERE `entry` = 17167; +UPDATE `creature_template` SET `ScriptName` = 'mob_aran_blizzard' WHERE `entry` = 17161; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r149.sql b/src/bindings/scripts/sql/Updates/r149.sql new file mode 100644 index 00000000000..a6311a14e9c --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r149.sql @@ -0,0 +1 @@ +INSERT INTO `spell_script_target` VALUES (30834, 1, 17646); diff --git a/src/bindings/scripts/sql/Updates/r150.sql b/src/bindings/scripts/sql/Updates/r150.sql new file mode 100644 index 00000000000..eb1eb180622 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r150.sql @@ -0,0 +1,19 @@ +UPDATE `creature_template` SET `ScriptName` = 'boss_grand_warlock_nethekurse' WHERE `entry` = 16807; +UPDATE `creature_template` SET `ScriptName` = 'mob_nethe_shadowfissure' WHERE `entry` = 17471; + +UPDATE `creature_template` SET `scriptname`='mob_phoenix' WHERE `entry`='21362'; +UPDATE `creature_template` SET `scriptname`='mob_phoenix_egg' WHERE `entry`='21364'; + +UPDATE `item_template` SET `ScriptName` = 'vorenthals_presence' WHERE `entry`= 30259; + +UPDATE `creature_template` SET `ScriptName` = 'boss_hydromancer_thespia' WHERE `entry` = 17797; +UPDATE `creature_template` SET `ScriptName` = 'mob_coilfang_waterelemental' WHERE `entry` = 17917; + +UPDATE `creature_template` SET `ScriptName` = 'mob_ethereal_beacon' WHERE `entry` = 18431; +UPDATE `creature_template` SET `ScriptName` = 'mob_ethereal_apprentice' WHERE `entry` = 18430; + +UPDATE `creature_template` SET `ScriptName` = 'boss_tavarok' WHERE `entry` = 18343; +UPDATE `creature_template` SET `ScriptName` = 'boss_pandemonius' WHERE `entry` = 18341; +UPDATE `creature_template` SET `ScriptName` = 'boss_nexusprince_shaffar' WHERE `entry` = 18344; + +UPDATE `creature_template` SET `ScriptName` = 'boss_ghazan' WHERE `entry` = 18105; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r152.sql b/src/bindings/scripts/sql/Updates/r152.sql new file mode 100644 index 00000000000..9efdb9f53c4 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r152.sql @@ -0,0 +1,12 @@ +UPDATE `creature_template` SET `ScriptName` = 'mob_syth_fire' WHERE `entry` = 19203; +UPDATE `creature_template` SET `ScriptName` = 'mob_syth_arcane' WHERE `entry` = 19205; +UPDATE `creature_template` SET `ScriptName` = 'mob_syth_frost' WHERE `entry` = 19204; +UPDATE `creature_template` SET `ScriptName` = 'mob_syth_shadow' WHERE `entry` = 19206; + +UPDATE `creature_template` SET `ScriptName` = 'boss_tailonking_ikiss' WHERE `entry` = 18473; +UPDATE `creature_template` SET `ScriptName` = 'boss_darkweaver_syth' WHERE `entry` = 18472; + +UPDATE creature_template SET ScriptName = 'npc_shattrathflaskvendors' WHERE entry = '23483'; +UPDATE creature_template SET ScriptName = 'npc_shattrathflaskvendors' WHERE entry = '23484'; + +UPDATE `creature_template` SET ScriptName='innkeeper' WHERE `npcflag` & 128<>0; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r153.sql b/src/bindings/scripts/sql/Updates/r153.sql new file mode 100644 index 00000000000..fb59c996edf --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r153.sql @@ -0,0 +1,33 @@ +-- Fishing net +UPDATE item_template SET ScriptName = 'draenei_fishing_net' WHERE entry = 23654; + +-- Auchenai Crypts +UPDATE `creature_template` set `scriptname` = 'boss_shirrak_the_dead_watcher' where `entry` = '18371'; +UPDATE `creature_template` set `scriptname` = 'boss_shirrak_the_dead_watcher' where `entry` = '20318'; + +-- Old Hillsbrad +UPDATE `creature_template` set `scriptname` = 'mob_lieutenant_drake' where `entry` = '17848'; +UPDATE `creature_template` set `scriptname` = 'mob_epoch_hunter' where `entry` = '18096'; +UPDATE `creature_template` SET `ScriptName` = 'boss_captain_skarloc' WHERE `entry`=17862; + +-- The Black Morass +UPDATE `creature_template` set `ScriptName` = 'boss_chrono_lord_deja' where `entry` = '17879'; +UPDATE `creature_template` SET `ScriptName` = 'boss_aeonus' WHERE `entry`=17881; +UPDATE `creature_template` set `ScriptName` = 'boss_temporus' where `entry` = '17880'; + +-- Deadmines +UPDATE `creature_template` SET `ScriptName` = 'boss_deadmines' WHERE `entry`=645; +UPDATE `creature_template` SET `ScriptName` = 'boss_deadmines' WHERE `entry`=1763; +UPDATE `creature_template` SET `ScriptName` = 'boss_deadmines' WHERE `entry`=599; +UPDATE `creature_template` SET `ScriptName` = 'boss_deadmines' WHERE `entry`=3586; + +-- Wailing Caverns +UPDATE `creature_template` SET `ScriptName` = 'boss_wailing_caverns' WHERE `entry`=3654; + +-- Gruul's Lair +UPDATE `creature_template` SET `ScriptName` = 'boss_high_king_maulgar' WHERE `entry`=18831; +UPDATE `creature_template` SET `ScriptName` = 'boss_kiggler_the_crazed' WHERE `entry`=18835; +UPDATE `creature_template` SET `ScriptName` = 'boss_blindeye_the_seer' WHERE `entry`=18836; +UPDATE `creature_template` SET `ScriptName` = 'boss_olm_the_summoner' WHERE `entry`=18834; +UPDATE `creature_template` SET `ScriptName` = 'boss_krosh_firehand' WHERE `entry`=18832; +UPDATE `instance_template` SET `script`="instance_gruuls_lair" WHERE `map`=565; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r157.sql b/src/bindings/scripts/sql/Updates/r157.sql new file mode 100644 index 00000000000..05c8299529c --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r157.sql @@ -0,0 +1,7 @@ +UPDATE `creature_template` SET `ScriptName` = 'boss_hydross_the_unstable' WHERE `entry`=21216; +UPDATE `creature_template` SET `ScriptName` = 'boss_leotheras_the_blind' WHERE `entry`=21215; + +-- Magtheridon's Lair +UPDATE `gameobject_template` SET `ScriptName`='go_manticron_cube' WHERE `entry`='181713'; +UPDATE `creature_template` SET `ScriptName` = 'boss_magtheridon' WHERE `entry`=17257; +UPDATE `creature_template` SET `ScriptName` = 'mob_hellfire_channeler' WHERE `entry`=17256; diff --git a/src/bindings/scripts/sql/Updates/r161.sql b/src/bindings/scripts/sql/Updates/r161.sql new file mode 100644 index 00000000000..0bb31ee2ba0 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r161.sql @@ -0,0 +1,12 @@ +-- shadow labyrinth +UPDATE `instance_template` SET `script` = 'instance_shadow_labyrinth' WHERE `map`= 555; +UPDATE `creature_template` SET `ScriptName` = 'boss_murmur' WHERE `entry` = 18708; +UPDATE `creature_template` SET `ScriptName` = 'boss_ambassador_hellmaw' WHERE `entry` = 18731; +UPDATE `creature_template` SET `ScriptName` = 'boss_grandmaster_vorpil' WHERE `entry` = 18732; +UPDATE `creature_template` SET `ScriptName` = 'boss_blackheart_the_inciter' WHERE `entry` = 18667; + +-- fathomlord karathress +UPDATE `creature_template` SET `ScriptName` = 'boss_fathomlord_karathress' WHERE `entry`=21214; +UPDATE `creature_template` SET `ScriptName` = 'boss_fathomguard_sharkkis' WHERE `entry`=21966; +UPDATE `creature_template` SET `ScriptName` = 'boss_fathomguard_tidalvess' WHERE `entry`=21965; +UPDATE `creature_template` SET `ScriptName` = 'boss_fathomguard_caribdis' WHERE `entry`=21964; diff --git a/src/bindings/scripts/sql/Updates/r163.sql b/src/bindings/scripts/sql/Updates/r163.sql new file mode 100644 index 00000000000..9f8253e3cd3 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r163.sql @@ -0,0 +1,2 @@ +UPDATE creature_template SET npcflag=npcflag|1, ScriptName='npc_cairne_bloodhoof' WHERE entry=3057; +UPDATE `gameobject_template` SET `ScriptName`='go_barov_journal' WHERE `entry`=180794; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r164.sql b/src/bindings/scripts/sql/Updates/r164.sql new file mode 100644 index 00000000000..30b7a6a05a6 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r164.sql @@ -0,0 +1,10 @@ +-- gruuls lair trash mobs +UPDATE `creature_template` SET `ScriptName` = 'mob_gruuls_lair' WHERE `entry` IN (19389, 21350); + +-- morogrim tidewalker +UPDATE `creature_template` SET `ScriptName` = 'boss_morogrim_tidewalker' WHERE `entry`=21213; +-- Water Globule +UPDATE `creature_template` SET `ScriptName` = 'mob_water_globule' WHERE `entry`=21913; + +-- Serpentshrine Cavern Trash Mobs +UPDATE `creature_template` SET `ScriptName` = 'mob_serpentshrine_cavern' WHERE `entry` IN (21246, 21339, 21221, 21224, 21227, 21228, 21226, 21225, 21298, 21299); \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r165.sql b/src/bindings/scripts/sql/Updates/r165.sql new file mode 100644 index 00000000000..69401cdb8f4 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r165.sql @@ -0,0 +1,8 @@ + +-- the steam vault +UPDATE `instance_template` SET `script` = 'instance_steam_vault' WHERE `map` = 545; + +-- right old hillsbrad foothills bosses script name +UPDATE `creature_template` SET `ScriptName` = 'boss_lieutenant_drake' WHERE `entry` = 17848; +UPDATE `creature_template` SET `ScriptName` = 'boss_epoch_hunter' WHERE `entry` = 18096; + diff --git a/src/bindings/scripts/sql/Updates/r169.sql b/src/bindings/scripts/sql/Updates/r169.sql new file mode 100644 index 00000000000..d5511e82106 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r169.sql @@ -0,0 +1,4 @@ +-- hellfire peninsula special taxi path +UPDATE `creature_template` SET `ScriptName` = 'npc_wing_commander_brack' WHERE `entry` = 19401; +UPDATE `creature_template` SET `ScriptName` = 'npc_wing_commander_dabiree' WHERE `entry` = 19409; +UPDATE `creature_template` SET `ScriptName` = 'npc_gryphoneer_windbellow' WHERE `entry` = 20235; diff --git a/src/bindings/scripts/sql/Updates/r170.sql b/src/bindings/scripts/sql/Updates/r170.sql new file mode 100644 index 00000000000..c397c160065 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r170.sql @@ -0,0 +1,2 @@ +-- special taxi path netherstorm +UPDATE `creature_template` SET `ScriptName` = 'npc_protectorate_nether_drake' WHERE `entry` = 20903; diff --git a/src/bindings/scripts/sql/Updates/r171.sql b/src/bindings/scripts/sql/Updates/r171.sql new file mode 100644 index 00000000000..5a7adb61514 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r171.sql @@ -0,0 +1,2 @@ +-- multi kill quest mobs hfp +UPDATE `creature_template` SET `ScriptName` = 'mobs_shattered_hand_orc' WHERE `entry` IN (19411, 19410, 19413, 19414, 16867, 19295, 16870, 16878, 19415); diff --git a/src/bindings/scripts/sql/Updates/r172.sql b/src/bindings/scripts/sql/Updates/r172.sql new file mode 100644 index 00000000000..4cac983026d --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r172.sql @@ -0,0 +1,3 @@ +-- support multikill bladespire and bloodmaul ogres +UPDATE `creature_template` SET `ScriptName` = 'mobs_bladespire_ogre' WHERE `entry` IN (19998, 20334, 21296, 21975); +UPDATE `creature_template` SET `ScriptName` = 'mobs_bloodmaul_ogre' WHERE `entry` IN (19948, 19952, 19957); diff --git a/src/bindings/scripts/sql/Updates/r174.sql b/src/bindings/scripts/sql/Updates/r174.sql new file mode 100644 index 00000000000..57dde5a0fd2 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r174.sql @@ -0,0 +1,8 @@ +-- multikill creatures +UPDATE `creature_template` SET `ScriptName` = 'mobs_dragonmaw_orc' WHERE `entry` IN (21717, 21718, 21719, 21720, 22331); +UPDATE `creature_template` SET `ScriptName` = 'mobs_shadowmoon_valley_wildlife' WHERE `entry` IN (21878, 21879); +UPDATE `creature_template` SET `ScriptName` = 'mobs_gordunni_ogre' WHERE `entry` IN (22143, 22144, 22148, 23022); +-- vendor +UPDATE `creature_template` SET `ScriptName` = 'npc_drake_dealer_hurlunk' WHERE `entry` = 23489; +UPDATE `creature_template` SET `ScriptName` = 'npc_rivern_frostwind' WHERE `entry` = 10618; + diff --git a/src/bindings/scripts/sql/Updates/r176.sql b/src/bindings/scripts/sql/Updates/r176.sql new file mode 100644 index 00000000000..58e75375c12 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r176.sql @@ -0,0 +1,5 @@ +-- tales and stories +UPDATE `creature_template` SET `ScriptName` = 'npc_tirion_fordring' WHERE `entry` = 1855; +UPDATE `creature_template` SET `ScriptName` = 'npc_loramus_thalipedes' WHERE `entry` = 7783; +UPDATE `creature_template` SET `ScriptName` = 'npc_fallen_hero_of_horde' WHERE `entry` = 7572; +UPDATE `creature_template` SET `ScriptName` = 'npc_lorax' WHERE `entry` = 10918; diff --git a/src/bindings/scripts/sql/Updates/r177.sql b/src/bindings/scripts/sql/Updates/r177.sql new file mode 100644 index 00000000000..3914679aa3b --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r177.sql @@ -0,0 +1,2 @@ +-- kalaran windblade +UPDATE `creature_template` SET `ScriptName` = 'npc_kalaran_windblade' WHERE `entry` = 8479; diff --git a/src/bindings/scripts/sql/Updates/r178.sql b/src/bindings/scripts/sql/Updates/r178.sql new file mode 100644 index 00000000000..f20a4366885 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r178.sql @@ -0,0 +1,2 @@ +-- kharan mighthammer +UPDATE `creature_template` SET `ScriptName` = 'npc_kharan_mighthammer' WHERE `entry` = 9021; diff --git a/src/bindings/scripts/sql/Updates/r181.sql b/src/bindings/scripts/sql/Updates/r181.sql new file mode 100644 index 00000000000..4807582d1a4 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r181.sql @@ -0,0 +1,2 @@ +-- npc_lantresor_of_the_blade +UPDATE `creature_template` SET `ScriptName` = 'npc_lantresor_of_the_blade' WHERE `entry` = 18261; diff --git a/src/bindings/scripts/sql/Updates/r182.sql b/src/bindings/scripts/sql/Updates/r182.sql new file mode 100644 index 00000000000..5d0a3c68fd2 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r182.sql @@ -0,0 +1,2 @@ +-- npc_ragged_john +UPDATE `creature_template` SET `ScriptName` = 'npc_ragged_john' WHERE `entry` = 9563; diff --git a/src/bindings/scripts/sql/Updates/r183.sql b/src/bindings/scripts/sql/Updates/r183.sql new file mode 100644 index 00000000000..79c175d0996 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r183.sql @@ -0,0 +1,5 @@ +-- revert to original specialflags + flag 4 for mangos support, script no longer needed for quest +UPDATE `quest_template` SET `SpecialFlags`= 140 WHERE `entry` IN (10040, 10041); +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` IN (18716, 18717, 18719); +-- gossip only, key to searing gorge discussion +UPDATE `creature_template` SET `ScriptName` = 'npc_mountaineer_pebblebitty' WHERE `entry` = 3836; diff --git a/src/bindings/scripts/sql/Updates/r184.sql b/src/bindings/scripts/sql/Updates/r184.sql new file mode 100644 index 00000000000..599fd9f3c0e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r184.sql @@ -0,0 +1,2 @@ +-- revert to default database values +UPDATE `quest_template` SET `SpecialFlags`= 10, `ReqCreatureOrGOId1` = 0, `ReqCreatureOrGOId2` = 0, `ReqCreatureOrGOId3`= 0, `ReqCreatureOrGOId4`= 0, `ReqCreatureOrGOCount1` = 0, `ReqCreatureOrGOCount2` = 0, `ReqCreatureOrGOCount3` = 0, `ReqCreatureOrGOCount4`= 0, `ReqSpellCast1` = 0, `ReqSpellCast2` = 0, `ReqSpellCast3` = 0, `ReqSpellCast4` = 0 WHERE `entry` IN (5929, 5930); diff --git a/src/bindings/scripts/sql/Updates/r186.sql b/src/bindings/scripts/sql/Updates/r186.sql new file mode 100644 index 00000000000..a225e6850fc --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r186.sql @@ -0,0 +1,2 @@ +-- leotheras the blind demonform +UPDATE `creature_template` SET `ScriptName` = 'boss_leotheras_the_blind_demonform' WHERE `entry` = 21845; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r187.sql b/src/bindings/scripts/sql/Updates/r187.sql new file mode 100644 index 00000000000..2b34b1c84bf --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r187.sql @@ -0,0 +1,12 @@ +-- test of lore quest npcs +UPDATE `creature_template` SET `ScriptName` = 'npc_parqual_fintallas' WHERE `entry` = 4488; +UPDATE `creature_template` SET `ScriptName` = 'npc_braug_dimspirit' WHERE `entry` = 4489; +-- quest Scratches +UPDATE `creature_template` SET `ScriptName` = 'npc_daranelle' WHERE `entry` = 21469; + +-- no comments of malfuntion, implementing mount vendors script +UPDATE `creature_template` SET `ScriptName` = 'npc_mount_vendor' WHERE `entry` IN (384, 1261, 1460, 2357, 3362, 3685, 4730, 4731, 4885, 7952, 7955, 16264, 17584); + +-- one time update, this is database related sql and should be included in any database +UPDATE `creature_template` SET `npcflag` = `npcflag`|1 WHERE `entry` IN (4488, 4489, 21469); +INSERT IGNORE `spell_teleport` VALUES (6766, 1,-2354.03,-1902.07,95.78,4.6); diff --git a/src/bindings/scripts/sql/Updates/r189.sql b/src/bindings/scripts/sql/Updates/r189.sql new file mode 100644 index 00000000000..51fc09abcd2 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r189.sql @@ -0,0 +1,3 @@ +UPDATE `gameobject_template` SET `ScriptName` = 'go_field_repair_bot' where `entry`=179552; +UPDATE `creature_template` SET `ScriptName` = 'boss_doomwalker' WHERE `entry` = 17711; +UPDATE `creature_template` SET `ScriptName` = 'boss_exarch_maladaar' WHERE `entry` = 18373; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r191.sql b/src/bindings/scripts/sql/Updates/r191.sql new file mode 100644 index 00000000000..f5d563bc272 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r191.sql @@ -0,0 +1,5 @@ + +/* THE MECHANAR */ +UPDATE `creature_template` SET `ScriptName` = 'boss_gatewatcher_iron_hand' WHERE `entry` = 19710; +UPDATE `creature_template` SET `ScriptName` = 'boss_nethermancer_sepethrea' WHERE `entry` = 19221; +UPDATE `creature_template` SET `ScriptName` = 'mob_ragin_flames' WHERE `entry` = 20481; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r192.sql b/src/bindings/scripts/sql/Updates/r192.sql new file mode 100644 index 00000000000..beb8aa31eb8 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r192.sql @@ -0,0 +1,9 @@ +-- lady vashj event +UPDATE `creature_template` SET `ScriptName` = 'boss_lady_vashj' WHERE `entry` = 21212; +UPDATE `creature_template` SET `ScriptName` = 'mob_enchanted_elemental' WHERE `entry` = 21958; +UPDATE `creature_template` SET `ScriptName` = 'mob_tainted_elemental' WHERE `entry` = 22009; +UPDATE `creature_template` SET `ScriptName` = 'mob_coilfang_elite' WHERE `entry` = 22055; +UPDATE `creature_template` SET `ScriptName` = 'mob_coilfang_strider' WHERE `entry` = 22056; +UPDATE `creature_template` SET `ScriptName` = 'mob_fathom_sporebat' WHERE `entry` = 22120; + +UPDATE `gameobject_template` SET `ScriptName` = 'go_shield_generator' WHERE `entry` IN (185052, 185053, 185051, 185054); diff --git a/src/bindings/scripts/sql/Updates/r197.sql b/src/bindings/scripts/sql/Updates/r197.sql new file mode 100644 index 00000000000..800595abd59 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r197.sql @@ -0,0 +1,5 @@ +-- scholomance script +UPDATE `instance_template` SET `script` = 'instance_scholomance' WHERE `map` = 289; + +-- arcatraz script +UPDATE `instance_template` SET `script` = 'instance_arcatraz' WHERE `map` = 552; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r204.sql b/src/bindings/scripts/sql/Updates/r204.sql new file mode 100644 index 00000000000..960ba366f18 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r204.sql @@ -0,0 +1,7 @@ +-- lady vashj event +UPDATE `gameobject_template` SET `type`=0,`ScriptName`='' WHERE `entry` IN (185052, 185053, 185051, 185054); +UPDATE `item_template` SET `ScriptName` = 'item_tainted_core' WHERE `entry` = 31088; +UPDATE `creature_template` SET `ScriptName` = 'mob_shield_generator_channel' WHERE `entry` = 19870; + +-- BWL trash mobs +UPDATE `creature_template` SET `ScriptName` = 'mob_blackwing_lair' WHERE `entry` IN (14265, 12457, 13996, 12459, 14261, 14263, 12467, 12463, 12461, 12464, 12460, 12465, 14262, 14264); diff --git a/src/bindings/scripts/sql/Updates/r206.sql b/src/bindings/scripts/sql/Updates/r206.sql new file mode 100644 index 00000000000..9adcb76a6a3 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r206.sql @@ -0,0 +1,16 @@ +-- Zul'Gurub Bosses Part 1 + Trash Mobs +UPDATE `creature_template` SET `ScriptName` = 'boss_jeklik' WHERE `entry` = 14517; +UPDATE `creature_template` SET `ScriptName` = 'boss_venoxis' WHERE `entry` = 14607; +UPDATE `creature_template` SET `ScriptName` = 'boss_marli' WHERE `entry` = 14510; +UPDATE `creature_template` SET `ScriptName` = 'boss_mandokir' WHERE `entry` = 11382; +UPDATE `creature_template` SET `ScriptName` = 'boss_gahzranka' WHERE `entry` = 15114; +UPDATE `creature_template` SET `ScriptName` = 'boss_jindo' WHERE `entry` = 11380; + +/* Spawn of Marli */ +UPDATE `creature_template` SET `ScriptName` = 'mob_spawn_of_marli' WHERE `entry` = 15041; +/* Jeklik Batrider */ +UPDATE `creature_template` SET `ScriptName` = 'mob_batrider' WHERE `entry` = 14965; + + +-- Darkmaster Ganling +UPDATE `creature_template` SET `ScriptName` = 'boss_darkmaster_gandling' WHERE `entry` = 1853; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r211.sql b/src/bindings/scripts/sql/Updates/r211.sql new file mode 100644 index 00000000000..ceb91171b9a --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r211.sql @@ -0,0 +1,6 @@ +-- kael'thas event +UPDATE `creature_template` SET `ScriptName` = 'boss_kaelthas' WHERE `entry` = 19622; +UPDATE `creature_template` SET `ScriptName` = 'boss_thaladred_the_darkener' WHERE `entry` = 20064; +UPDATE `creature_template` SET `ScriptName` = 'boss_lord_sanguinar' WHERE `entry` = 20060; +UPDATE `creature_template` SET `ScriptName` = 'boss_grand_astromancer_capernian' WHERE `entry` = 20062; +UPDATE `creature_template` SET `ScriptName` = 'boss_master_engineer_telonicus' WHERE `entry` = 20063; diff --git a/src/bindings/scripts/sql/Updates/r213.sql b/src/bindings/scripts/sql/Updates/r213.sql new file mode 100644 index 00000000000..b512cfd5044 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r213.sql @@ -0,0 +1,11 @@ +-- Zul'Gurub Part II +UPDATE `creature_template` SET `ScriptName` = "boss_hakkar" WHERE `creature_template`.`entry` = 15295; +UPDATE `creature_template` SET `ScriptName` = "boss_thekal" WHERE `creature_template`.`entry` = 14509; +UPDATE `creature_template` SET `ScriptName` = "boss_arlokk" WHERE `creature_template`.`entry` = 14515; +UPDATE `creature_template` SET `ScriptName` = "mob_zealot_lorkhan" WHERE `creature_template`.`entry` = 11347; +UPDATE `creature_template` SET `ScriptName` = "mob_zealot_zath" WHERE `creature_template`.`entry` = 11348; +UPDATE `creature_template` SET `ScriptName` = "mob_healing_ward" WHERE `creature_template`.`entry` = 14987; +UPDATE `creature_template` SET `Scriptname` = "mobs_zulgurub" WHERE `creature_template`.`entry` IN (11340 ,11352 , 11350, 11830, 11372, 11351, 14750); + + + diff --git a/src/bindings/scripts/sql/Updates/r214.sql b/src/bindings/scripts/sql/Updates/r214.sql new file mode 100644 index 00000000000..24b5aa393d3 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r214.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName` = "boss_hakkar" WHERE `creature_template`.`entry` = 14834; + diff --git a/src/bindings/scripts/sql/Updates/r215.sql b/src/bindings/scripts/sql/Updates/r215.sql new file mode 100644 index 00000000000..90bfbf0e86b --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r215.sql @@ -0,0 +1 @@ +UPDATE `item_template` SET `ScriptName` = 'item_skin_of_purest_water' WHERE `entry` = 23751; diff --git a/src/bindings/scripts/sql/Updates/r218.sql b/src/bindings/scripts/sql/Updates/r218.sql new file mode 100644 index 00000000000..e6eeb7782aa --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r218.sql @@ -0,0 +1,5 @@ +-- misc quest related +UPDATE `creature_template` SET `ScriptName` = 'npc_restless_apparition' WHERE `entry` = 23861; +UPDATE `creature_template` SET `ScriptName` = 'mobs_direhorn_grimtotem' WHERE `entry` = 23595; +UPDATE `creature_template` SET `ScriptName` = 'mobs_risen_husk_spirit' WHERE `entry` IN (23554, 23555); + diff --git a/src/bindings/scripts/sql/Updates/r219.sql b/src/bindings/scripts/sql/Updates/r219.sql new file mode 100644 index 00000000000..8b01dd43784 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r219.sql @@ -0,0 +1,3 @@ +/* Fix for Wrong ZG Update */ +UPDATE `creature_template` SET `ScriptName` = ' ' WHERE `entry` = 14607; +UPDATE `creature_template` SET `ScriptName` = ' ' WHERE `entry` = 15295; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r221_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r221_scriptdev2.sql new file mode 100644 index 00000000000..06cd4334508 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r221_scriptdev2.sql @@ -0,0 +1 @@ +ALTER TABLE `eventai_Scripts` RENAME TO `eventai_scripts`; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r227_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r227_scriptdev2.sql new file mode 100644 index 00000000000..4e600fc3df8 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r227_scriptdev2.sql @@ -0,0 +1,29 @@ +ALTER TABLE `eventai_scripts` + DROP PRIMARY KEY, + DROP `id`, + ADD `id` int(11) unsigned NOT NULL COMMENT 'Identifier' PRIMARY KEY AUTO_INCREMENT FIRST, + + MODIFY `event_param1` int(11) signed NOT NULL default '0', + MODIFY `event_param2` int(11) signed NOT NULL default '0', + MODIFY `event_param3` int(11) signed NOT NULL default '0', + + MODIFY `action1_param1` int(11) signed NOT NULL default '0', + MODIFY `action1_param2` int(11) signed NOT NULL default '0', + MODIFY `action1_param3` int(11) signed NOT NULL default '0', + + MODIFY `action2_param1` int(11) signed NOT NULL default '0', + MODIFY `action2_param2` int(11) signed NOT NULL default '0', + MODIFY `action2_param3` int(11) signed NOT NULL default '0', + + MODIFY `action3_param1` int(11) signed NOT NULL default '0', + MODIFY `action3_param2` int(11) signed NOT NULL default '0', + MODIFY `action3_param3` int(11) signed NOT NULL default '0', + + ADD `comment` varchar(255) NOT NULL default '' COMMENT 'Event Comment'; + +ALTER TABLE `localized_texts` + DROP PRIMARY KEY, + DROP `id`, + ADD `id` int(11) unsigned NOT NULL COMMENT 'Identifier' PRIMARY KEY AUTO_INCREMENT FIRST, + ADD `comment` varchar(255) NOT NULL default '' COMMENT 'Text Comment'; + \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r230_mangos.sql b/src/bindings/scripts/sql/Updates/r230_mangos.sql new file mode 100644 index 00000000000..4db44512536 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r230_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_deserter_agitator' WHERE `entry` = 23602; +UPDATE `gameobject_template` SET `ScriptName` = 'go_field_repair_bot_74A' where `entry`= 179552; diff --git a/src/bindings/scripts/sql/Updates/r234_mangos.sql b/src/bindings/scripts/sql/Updates/r234_mangos.sql new file mode 100644 index 00000000000..8a72910adbe --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r234_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_screecher_spirit' WHERE `entry` = 8612; +UPDATE `item_template` SET `ScriptName` = 'item_yehkinyas_bramble' WHERE `entry`= 10699; diff --git a/src/bindings/scripts/sql/Updates/r237_mangos.sql b/src/bindings/scripts/sql/Updates/r237_mangos.sql new file mode 100644 index 00000000000..265bde22802 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r237_mangos.sql @@ -0,0 +1,6 @@ +-- new +UPDATE `creature_template` SET `ScriptName` = 'guard_azuremyst' WHERE `entry` = 18038; +UPDATE `creature_template` SET `ScriptName` = 'guard_eversong' WHERE `entry` = 16221; +-- update +UPDATE `creature_template` SET `ScriptName` = 'guard_durotar' WHERE `entry` = 5953; +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` IN (3210, 3211, 3213, 3214, 1736, 1739, 1737); diff --git a/src/bindings/scripts/sql/Updates/r238_mangos.sql b/src/bindings/scripts/sql/Updates/r238_mangos.sql new file mode 100644 index 00000000000..d148e11f863 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r238_mangos.sql @@ -0,0 +1,2 @@ +-- revert previously bad sql +UPDATE `creature_template` SET `ScriptName` = '' WHERE `ScriptName` LIKE '%innkeeper%' AND `npcflag` & 128<>0; diff --git a/src/bindings/scripts/sql/Updates/r239_mangos.sql b/src/bindings/scripts/sql/Updates/r239_mangos.sql new file mode 100644 index 00000000000..8936e4d8f6c --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r239_mangos.sql @@ -0,0 +1 @@ +UPDATE `item_template` SET `ScriptName` = '' WHERE `entry` = 23268; diff --git a/src/bindings/scripts/sql/Updates/r240_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r240_scriptdev2.sql new file mode 100644 index 00000000000..635ee46b18e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r240_scriptdev2.sql @@ -0,0 +1,2 @@ +ALTER TABLE `eventai_scripts` +ADD `event_inverse_phase_mask` int(11) signed NOT NULL default '0', COMMENT 'Mask which phases this event will not trigger in'; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r241_mangos.sql b/src/bindings/scripts/sql/Updates/r241_mangos.sql new file mode 100644 index 00000000000..ffc99c200d5 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r241_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_skyguard_handler_irena' WHERE `entry` = 23413; +UPDATE `creature_template` SET `ScriptName` = 'npc_skyguard_handler_deesak' WHERE `entry` = 23415; diff --git a/src/bindings/scripts/sql/Updates/r242_mangos.sql b/src/bindings/scripts/sql/Updates/r242_mangos.sql new file mode 100644 index 00000000000..49465a656e9 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r242_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_zamael_lunthistle' WHERE `entry` = 8436; +UPDATE `creature_template` SET `ScriptName` = 'npc_lothos_riftwaker' WHERE `entry` = 14387; diff --git a/src/bindings/scripts/sql/Updates/r243_mangos.sql b/src/bindings/scripts/sql/Updates/r243_mangos.sql new file mode 100644 index 00000000000..6b1ad08d0a0 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r243_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_marin_noggenfogger' WHERE `entry` = 7564; diff --git a/src/bindings/scripts/sql/Updates/r249_mangos.sql b/src/bindings/scripts/sql/Updates/r249_mangos.sql new file mode 100644 index 00000000000..cf0c36ef468 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r249_mangos.sql @@ -0,0 +1,3 @@ +UPDATE `creature_template` SET `ScriptName` = 'mob_flamewaker_priest' WHERE `entry` = 11662; +UPDATE `creature_template` SET `ScriptName` = 'boss_datrohan_balnazzar' WHERE `entry` = 10812; + diff --git a/src/bindings/scripts/sql/Updates/r250_mangos.sql b/src/bindings/scripts/sql/Updates/r250_mangos.sql new file mode 100644 index 00000000000..841e39d71b3 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r250_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName` = 'boss_dathrohan_balnazzar' WHERE `entry` = 10812; + \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r253_mangos.sql b/src/bindings/scripts/sql/Updates/r253_mangos.sql new file mode 100644 index 00000000000..424237be238 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r253_mangos.sql @@ -0,0 +1 @@ +UPDATE `item_template` SET `ScriptName` = 'item_zezzaks_shard' WHERE `entry` = 31463; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r255_mangos.sql b/src/bindings/scripts/sql/Updates/r255_mangos.sql new file mode 100644 index 00000000000..7019586eb96 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r255_mangos.sql @@ -0,0 +1,5 @@ +UPDATE instance_template SET script = 'instance_zulaman' WHERE map = 568; +UPDATE creature_template SET scriptname='boss_janalai' WHERE entry=23578; +UPDATE creature_template SET scriptname='mob_jandalai_firebomb' WHERE entry=23920; +UPDATE creature_template SET scriptname='mob_amanishi_hatcher' WHERE entry=23818; +UPDATE creature_template SET scriptname='mob_hatchling' WHERE entry=23598; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r256_mangos.sql b/src/bindings/scripts/sql/Updates/r256_mangos.sql new file mode 100644 index 00000000000..4207454117e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r256_mangos.sql @@ -0,0 +1,35 @@ +UPDATE creature_template SET ScriptName = 'npc_tyrande_whisperwind' WHERE entry = 17948; +UPDATE creature_template SET ScriptName = 'npc_thrall' WHERE entry = 17852; +UPDATE creature_template SET ScriptName = 'npc_jaina_proudmoore' WHERE entry = 17772; + +UPDATE creature_template SET ScriptName = 'npc_akama_shade' WHERE entry = 23089; -- Akama at Shade of Akama +UPDATE creature_template SET ScriptName = 'npc_akama_illidan' WHERE entry = 22990; -- Akama at Illidan +UPDATE creature_template SET ScriptName = 'illidari_council' WHERE entry = 23426; -- Illidari Council controller mob +UPDATE creature_template SET ScriptName = 'boss_veras_darkshadow' WHERE entry = 22952; -- Rogue of Illidari Council +UPDATE creature_template SET ScriptName = 'boss_teron_gorefiend' WHERE entry = 22871; -- Teron Gorefiend +UPDATE creature_template SET ScriptName = 'boss_supremus' WHERE entry = 22898; -- Supremus +UPDATE creature_template SET ScriptName = 'boss_shade_of_akama' WHERE entry = 22841; -- Shade of Akama +UPDATE creature_template SET ScriptName = 'boss_reliquary_of_souls' WHERE entry = 22856; -- Reliquary Controller Mob +UPDATE creature_template SET ScriptName = 'boss_essence_of_suffering' WHERE entry = 23418; -- Essence Of Suffering +UPDATE creature_template SET ScriptName = 'boss_essence_of_desire' WHERE entry = 23419; -- Essence of Desire +UPDATE creature_template SET ScriptName = 'boss_essence_of_anger' WHERE entry = 23420; -- Essence of Anger +UPDATE creature_template SET ScriptName = 'boss_najentus' WHERE entry = 22887; -- High Warlord Naj'entus +UPDATE creature_template SET ScriptName = 'boss_gurtogg_bloodboil' WHERE entry = 22948; -- Gurtogg Bloodboil +UPDATE creature_template SET ScriptName = 'boss_mother_shahraz' WHERE entry = 22947; -- Mother Shahraz +UPDATE creature_template SET ScriptName = 'boss_lady_malande' WHERE entry = 22951; -- Priest <3 at Illidari Council +UPDATE creature_template SET ScriptName = 'boss_illidan_stormrage' WHERE entry = 22917; -- Illidan The Betrayer! +UPDATE creature_template SET ScriptName = 'boss_high_nethermancer_zerevor' WHERE entry = 22950; -- Mage at Illidari Council +UPDATE creature_template SET ScriptName = 'boss_gathios_the_shatterer' WHERE entry = 22949; -- Paladin at Illidari Council +UPDATE creature_template SET ScriptName = 'boss_maiev_shadowsong' WHERE entry = 23197; -- Maiev Shadowsong +UPDATE creature_template SET ScriptName = 'mob_blaze' WHERE entry = 23259; -- Blaze mob in Illidan Phase 2 +UPDATE creature_template SET ScriptName = 'mob_flame_of_azzinoth' WHERE entry = 22997; -- Flame of Azzinoth (Illidan Phase 2) +UPDATE creature_template SET ScriptName = 'mob_demon_fire' WHERE entry = 23069; -- Demon Fire in Illidan Phase 2 +UPDATE creature_template SET ScriptName = 'mob_flame_crash' WHERE entry = 23336; -- Flame Crash in Illidan Normal Form +UPDATE creature_template SET ScriptName = 'mob_cage_trap_trigger' WHERE entry = 23304; -- Cage Trap mob in Illidan Phase 3/4 Normal +UPDATE creature_template SET ScriptName = 'mob_shadow_demon' WHERE entry = 23375; -- Shadow Demon in Illidan Demon Form +UPDATE creature_template SET ScriptName = 'npc_volcano' WHERE entry = 23085; -- Supremus Volcano +UPDATE creature_template SET ScriptName = 'molten_flame' WHERE entry = 23095; -- Molten Flame in SUpremus +UPDATE creature_template SET ScriptName = 'mob_ashtongue_channeler' WHERE entry = 23421; -- Ashtongue CHanneler in Shade of AKama +UPDATE creature_template SET ScriptName = 'mob_ashtongue_sorcerer' WHERE entry = 23215; -- Ashtongue Sorcerer in Shade of Akama +UPDATE creature_template SET ScriptName = 'npc_enslaved_soul' WHERE entry = 23469; -- Enslaved Soul in Reliquary Event +-- UPDATE creature_template SET ScriptName = 'mob_najentus_spine' WHERE entry = 500000; -- Workaround creature for spine in Najentus event \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r257_mangos.sql b/src/bindings/scripts/sql/Updates/r257_mangos.sql new file mode 100644 index 00000000000..1bed2c9f098 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r257_mangos.sql @@ -0,0 +1,4 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_archmage_malin' WHERE `entry` = 2708; +UPDATE `creature_template` SET `ScriptName` = 'npc_dashel_stonefist' WHERE `entry` = 4961; +UPDATE `creature_template` SET `ScriptName` = 'npc_doctor' WHERE `entry` IN (12939, 12920); +UPDATE `creature_template` SET `ScriptName` = 'npc_injured_patient' WHERE `entry` IN (12936, 12937, 12938, 12923, 12924, 12925); diff --git a/src/bindings/scripts/sql/Updates/r258_mangos.sql b/src/bindings/scripts/sql/Updates/r258_mangos.sql new file mode 100644 index 00000000000..18970d669da --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r258_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_deathly_usher' WHERE `entry` = 8816; diff --git a/src/bindings/scripts/sql/Updates/r260_mangos.sql b/src/bindings/scripts/sql/Updates/r260_mangos.sql new file mode 100644 index 00000000000..e96598d09e1 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r260_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_lady_katrana_prestor' WHERE `entry` = 1749; diff --git a/src/bindings/scripts/sql/Updates/r261_mangos.sql b/src/bindings/scripts/sql/Updates/r261_mangos.sql new file mode 100644 index 00000000000..b1c8e6312a9 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r261_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_general_marcus_jonathan' WHERE `entry` = 466; diff --git a/src/bindings/scripts/sql/Updates/r262_mangos.sql b/src/bindings/scripts/sql/Updates/r262_mangos.sql new file mode 100644 index 00000000000..225b7c38437 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r262_mangos.sql @@ -0,0 +1,3 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_royal_historian_archesonus' WHERE `entry` = 8879; +UPDATE `creature_template` SET `ScriptName` = 'npc_bunthen_plainswind' WHERE `entry` = 11798; +UPDATE `creature_template` SET `ScriptName` = 'npc_silva_filnaveth' WHERE `entry` = 11800; diff --git a/src/bindings/scripts/sql/Updates/r263_mangos.sql b/src/bindings/scripts/sql/Updates/r263_mangos.sql new file mode 100644 index 00000000000..a3ff56a0bd7 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r263_mangos.sql @@ -0,0 +1,3 @@ +UPDATE `gameobject_template` SET `ScriptName` = 'go_northern_crystal_pylon' WHERE `entry` = 164955; +UPDATE `gameobject_template` SET `ScriptName` = 'go_western_crystal_pylon' WHERE `entry` = 164956; +UPDATE `gameobject_template` SET `ScriptName` = 'go_eastern_crystal_pylon' WHERE `entry` = 164957; diff --git a/src/bindings/scripts/sql/Updates/r264_mangos.sql b/src/bindings/scripts/sql/Updates/r264_mangos.sql new file mode 100644 index 00000000000..410f1de42d5 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r264_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_astor_hadren' WHERE `entry` = 6497; diff --git a/src/bindings/scripts/sql/Updates/r265_mangos.sql b/src/bindings/scripts/sql/Updates/r265_mangos.sql new file mode 100644 index 00000000000..4eaed94ee10 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r265_mangos.sql @@ -0,0 +1,9 @@ +UPDATE `item_template` SET `ScriptName` = 'item_nether_wraith_beacon' WHERE `entry` = 31742; +UPDATE `item_template` SET `ScriptName` = 'item_area_52_special' WHERE `entry` = 28132; +UPDATE `item_template` SET `ScriptName` = 'item_vorenthals_presence' WHERE `entry` = 30259; +UPDATE `item_template` SET `ScriptName` = 'item_draenei_fishing_net' WHERE `entry` = 23654; +UPDATE `item_template` SET `ScriptName` = 'item_skin_of_purest_water' WHERE `entry` = 23751; +UPDATE `item_template` SET `ScriptName` = 'item_yehkinyas_bramble' WHERE `entry` = 10699; +UPDATE `item_template` SET `ScriptName` = 'item_zezzaks_shard' WHERE `entry` = 31463; +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` IN (40100,40101,40102,40103,40104,40105,40106,40107,40108,40109,40110,40111,40112,40113); +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` IN (40200,40201,40202,40203,40204,40205,40206,40207,40208,40209,40210,40211,40212,40213); diff --git a/src/bindings/scripts/sql/Updates/r269_mangos.sql b/src/bindings/scripts/sql/Updates/r269_mangos.sql new file mode 100644 index 00000000000..95ed715301a --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r269_mangos.sql @@ -0,0 +1,6 @@ +-- removing old script names that does no longer exist +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` IN (12198,14982,857,12197,7427,347,15007,2804); +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` IN (9564,12136,3149,12137,9566,3150,9559,9558); +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` = 197; +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` IN (11328, 11260); +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` IN (80, 1236, 3578, 40, 1358, 1360, 5996, 1426, 1094, 674); diff --git a/src/bindings/scripts/sql/Updates/r270_mangos.sql b/src/bindings/scripts/sql/Updates/r270_mangos.sql new file mode 100644 index 00000000000..2d7ffe0c432 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r270_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_overseer_nuaar' WHERE `entry` = 21981; diff --git a/src/bindings/scripts/sql/Updates/r271_mangos.sql b/src/bindings/scripts/sql/Updates/r271_mangos.sql new file mode 100644 index 00000000000..74fcb02284d --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r271_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_greatmother_geyah' WHERE `entry` = 18141; diff --git a/src/bindings/scripts/sql/Updates/r272_mangos.sql b/src/bindings/scripts/sql/Updates/r272_mangos.sql new file mode 100644 index 00000000000..3e5313f20e0 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r272_mangos.sql @@ -0,0 +1,8 @@ +UPDATE `creature_template` SET `ScriptName` = 'boss_kaelthas' WHERE `entry` = 23054; +UPDATE `creature_template` SET `ScriptName` = 'boss_thaladred_the_darkener' WHERE `entry` = 20064; +UPDATE `creature_template` SET `ScriptName` = 'boss_lord_sanguinar' WHERE `entry` = 20060; +UPDATE `creature_template` SET `ScriptName` = 'boss_grand_astromancer_capernian' WHERE `entry` = 20062; +UPDATE `creature_template` SET `ScriptName` = 'boss_master_engineer_telonicus' WHERE `entry` = 20063; +UPDATE `creature_template` SET `ScriptName` = 'mob_phoenix' WHERE `entry` = 21362; +UPDATE `creature_template` SET `ScriptName` = 'mob_nether_vapor' WHERE `entry` = 21002; +UPDATE `creature_template` SET `ScriptName` = 'mob_kael_flamestrike' WHERE `entry` = 21369; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r273_mangos.sql b/src/bindings/scripts/sql/Updates/r273_mangos.sql new file mode 100644 index 00000000000..db98e282f09 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r273_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_saikkal_the_elder' WHERE `entry` = 22932; diff --git a/src/bindings/scripts/sql/Updates/r274_mangos.sql b/src/bindings/scripts/sql/Updates/r274_mangos.sql new file mode 100644 index 00000000000..fef98f84e11 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r274_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'mob_blackwing_lair' WHERE `entry` = 12557; diff --git a/src/bindings/scripts/sql/Updates/r275_mangos.sql b/src/bindings/scripts/sql/Updates/r275_mangos.sql new file mode 100644 index 00000000000..618666b80e1 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r275_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'mob_shade_of_jindo' WHERE `entry` = 14986; diff --git a/src/bindings/scripts/sql/Updates/r281_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r281_scriptdev2.sql new file mode 100644 index 00000000000..719d1ee7852 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r281_scriptdev2.sql @@ -0,0 +1,35 @@ +ALTER TABLE eventai_scripts +ADD event_chance tinyint(3) unsigned NOT NULL default 100 AFTER event_inverse_phase_mask; + +-- EVENT_T_TIMER_REPEAT +UPDATE eventai_scripts SET event_chance = -event_param3 WHERE event_type = 0 AND event_param3 < 0; + +-- EVENT_T_TIMER_SINGLE +UPDATE eventai_scripts SET event_chance = event_param2 WHERE event_type = 1; + +-- EVENT_T_TIMER_OOC_REPEAT +UPDATE eventai_scripts SET event_chance = -event_param3 WHERE event_type = 2 AND event_param3 < 0; + +-- EVENT_T_TIMER_OOC_SINGLE +UPDATE eventai_scripts SET event_chance = event_param2 WHERE event_type = 3; + +-- EVENT_T_HP_SINGLE +UPDATE eventai_scripts SET event_chance = -event_param3 WHERE event_type = 4 AND event_param3 < 0; + +-- EVENT_T_MANA_SINGLE +UPDATE eventai_scripts SET event_chance = -event_param3 WHERE event_type = 5 AND event_param3 < 0; + +-- EVENT_T_AGGRO +UPDATE eventai_scripts SET event_chance = event_param1 WHERE event_type = 6; + +-- EVENT_T_KILL +UPDATE eventai_scripts SET event_chance = event_param2 WHERE event_type = 7; + +-- EVENT_T_DEATH +UPDATE eventai_scripts SET event_chance = event_param1 WHERE event_type = 8; + +-- EVENT_T_EVADE +UPDATE eventai_scripts SET event_chance = event_param1 WHERE event_type = 9; + +-- EVENT_T_SPELLHIT +UPDATE eventai_scripts SET event_chance = event_param3 WHERE event_type = 10; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r282_mangos.sql b/src/bindings/scripts/sql/Updates/r282_mangos.sql new file mode 100644 index 00000000000..93ea058523a --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r282_mangos.sql @@ -0,0 +1,38 @@ +UPDATE creature_template SET ScriptName = 'npc_tyrande_whisperwind' WHERE entry = 17948; +UPDATE creature_template SET ScriptName = 'npc_thrall' WHERE entry = 17852; +UPDATE creature_template SET ScriptName = 'npc_jaina_proudmoore' WHERE entry = 17772; + +UPDATE creature_template SET ScriptName = 'npc_akama_shade' WHERE entry = 23089; -- Akama at Shade of Akama +UPDATE creature_template SET ScriptName = 'npc_akama_illidan' WHERE entry = 22990; -- Akama at Illidan +UPDATE creature_template SET ScriptName = 'illidari_council' WHERE entry = 23426; -- Illidari Council controller mob +UPDATE creature_template SET ScriptName = 'boss_veras_darkshadow' WHERE entry = 22952; -- Rogue of Illidari Council +UPDATE creature_template SET ScriptName = 'boss_teron_gorefiend' WHERE entry = 22871; -- Teron Gorefiend +UPDATE creature_template SET ScriptName = 'boss_supremus' WHERE entry = 22898; -- Supremus +UPDATE creature_template SET ScriptName = 'boss_shade_of_akama' WHERE entry = 22841; -- Shade of Akama +UPDATE creature_template SET ScriptName = 'boss_reliquary_of_souls' WHERE entry = 22856; -- Reliquary Controller Mob +UPDATE creature_template SET ScriptName = 'boss_essence_of_suffering' WHERE entry = 23418; -- Essence Of Suffering +UPDATE creature_template SET ScriptName = 'boss_essence_of_desire' WHERE entry = 23419; -- Essence of Desire +UPDATE creature_template SET ScriptName = 'boss_essence_of_anger' WHERE entry = 23420; -- Essence of Anger +UPDATE creature_template SET ScriptName = 'boss_najentus' WHERE entry = 22887; -- High Warlord Naj'entus +UPDATE creature_template SET ScriptName = 'boss_gurtogg_bloodboil' WHERE entry = 22948; -- Gurtogg Bloodboil +UPDATE creature_template SET ScriptName = 'boss_mother_shahraz' WHERE entry = 22947; -- Mother Shahraz +UPDATE creature_template SET ScriptName = 'boss_lady_malande' WHERE entry = 22951; -- Priest <3 at Illidari Council +UPDATE creature_template SET ScriptName = 'boss_illidan_stormrage' WHERE entry = 22917; -- Illidan The Betrayer! +UPDATE creature_template SET ScriptName = 'boss_high_nethermancer_zerevor' WHERE entry = 22950; -- Mage at Illidari Council +UPDATE creature_template SET ScriptName = 'boss_gathios_the_shatterer' WHERE entry = 22949; -- Paladin at Illidari Council +UPDATE creature_template SET ScriptName = 'boss_maiev_shadowsong' WHERE entry = 23197; -- Maiev Shadowsong +UPDATE creature_template SET ScriptName = 'mob_blaze' WHERE entry = 23259; -- Blaze mob in Illidan Phase 2 +UPDATE creature_template SET ScriptName = 'mob_flame_of_azzinoth' WHERE entry = 22997; -- Flame of Azzinoth (Illidan Phase 2) +UPDATE creature_template SET ScriptName = 'mob_blade_of_azzinoth' WHERE entry = 22996; -- Blade of Azzinoth (Illidan Phase 2) +UPDATE creature_template SET ScriptName = 'mob_demon_fire' WHERE entry = 23069; -- Demon Fire in Illidan Phase 2 +UPDATE creature_template SET ScriptName = 'mob_flame_crash' WHERE entry = 23336; -- Flame Crash in Illidan Normal Form +UPDATE creature_template SET ScriptName = 'mob_cage_trap_trigger' WHERE entry = 23304; -- Cage Trap mob in Illidan Phase 3/4 Normal +UPDATE creature_template SET ScriptName = 'mob_shadow_demon' WHERE entry = 23375; -- Shadow Demon in Illidan Demon Form +UPDATE creature_template SET ScriptName = 'npc_volcano' WHERE entry = 23085; -- Supremus Volcano +UPDATE creature_template SET ScriptName = 'molten_flame' WHERE entry = 23095; -- Molten Flame in SUpremus +UPDATE creature_template SET ScriptName = 'mob_ashtongue_channeler' WHERE entry = 23421; -- Ashtongue CHanneler in Shade of AKama +UPDATE creature_template SET ScriptName = 'mob_ashtongue_sorcerer' WHERE entry = 23215; -- Ashtongue Sorcerer in Shade of Akama +UPDATE creature_template SET ScriptName = 'npc_enslaved_soul' WHERE entry = 23469; -- Enslaved Soul in Reliquary Event +UPDATE creature_template SET ScriptName = 'mob_doom_blossom' WHERE entry = 23123; -- Doom Blossoms in Teron Gorefiend's encounter +-- UPDATE creature_template sET ScriptName = 'mob_shadowy_construct' WHERE entry = 23111; -- Shadowy Construct in Teron Gorefiend's encounter. Commented until Mind Control is implemented. +-- UPDATE creature_template SET ScriptName = 'mob_najentus_spine' WHERE entry = 500000; -- Workaround creature for spine in Najentus event diff --git a/src/bindings/scripts/sql/Updates/r286_mangos.sql b/src/bindings/scripts/sql/Updates/r286_mangos.sql new file mode 100644 index 00000000000..d1d0a14dfa5 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r286_mangos.sql @@ -0,0 +1,2 @@ +-- Items +UPDATE `item_template` SET `ScriptName` = '' WHERE `entry` = 23751; -- Remove script for Skin of Purest Water \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r289_mangos.sql b/src/bindings/scripts/sql/Updates/r289_mangos.sql new file mode 100644 index 00000000000..48e08ebb878 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r289_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `scriptname` = 'npc_creditmarker_visit_with_ancestors' WHERE `entry` IN (18840,18841,18842,18843); \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r291_mangos.sql b/src/bindings/scripts/sql/Updates/r291_mangos.sql new file mode 100644 index 00000000000..a85fb7bf34c --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r291_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'mob_spawn_of_fankriss' WHERE `entry` = 15630; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r295_mangos.sql b/src/bindings/scripts/sql/Updates/r295_mangos.sql new file mode 100644 index 00000000000..b992e0ee630 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r295_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `instance_template` SET `script` = 'instance_stratholme' WHERE `map` = 329; +UPDATE `creature_template` SET `ScriptName` = 'boss_silver_hand_bosses' WHERE `entry` IN (17910, 17911, 17912, 17913, 17914); diff --git a/src/bindings/scripts/sql/Updates/r297_mangos.sql b/src/bindings/scripts/sql/Updates/r297_mangos.sql new file mode 100644 index 00000000000..702013c465b --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r297_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_berthold' WHERE `entry` = 16153; diff --git a/src/bindings/scripts/sql/Updates/r298_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r298_scriptdev2.sql new file mode 100644 index 00000000000..953baee559b --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r298_scriptdev2.sql @@ -0,0 +1,2 @@ +-- EVENT_T_SPELLHIT +UPDATE eventai_scripts SET event_param3 = event_param2, event_param2 = -1 WHERE event_type = 10; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r299_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r299_scriptdev2.sql new file mode 100644 index 00000000000..8b835b0ed7c --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r299_scriptdev2.sql @@ -0,0 +1,9 @@ +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', +PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='EventAI Summoning Locations'; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r304_mangos.sql b/src/bindings/scripts/sql/Updates/r304_mangos.sql new file mode 100644 index 00000000000..2a6ed9c0f60 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r304_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` IN (15440, 15612); diff --git a/src/bindings/scripts/sql/Updates/r306_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r306_scriptdev2.sql new file mode 100644 index 00000000000..60eac88d01e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r306_scriptdev2.sql @@ -0,0 +1,4 @@ +DROP TABLE IF EXISTS `db_version`; +CREATE TABLE `db_version` ( +`version` varchar(255) NOT NULL default '' COMMENT 'Database version string' +) ENGINE=MyISAM DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r307_mangos.sql b/src/bindings/scripts/sql/Updates/r307_mangos.sql new file mode 100644 index 00000000000..e239d40700d --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r307_mangos.sql @@ -0,0 +1,4 @@ +UPDATE `creature_template` SET `ScriptName` = 'npcs_dithers_and_arbington' WHERE `entry` IN (11056, 11057); +UPDATE `creature_template` SET `ScriptName` = 'npc_witch_doctor_mauari' WHERE `entry` = 10307; +UPDATE `creature_template` SET `ScriptName` = 'npcs_riverbreeze_and_silversky' WHERE `entry` IN (9528, 9529); +UPDATE `creature_template` SET `ScriptName` = 'npc_sayge' WHERE `entry` = 14822; diff --git a/src/bindings/scripts/sql/Updates/r308_mangos.sql b/src/bindings/scripts/sql/Updates/r308_mangos.sql new file mode 100644 index 00000000000..b3c16a91402 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r308_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_nat_pagle' WHERE `entry` = 12919; diff --git a/src/bindings/scripts/sql/Updates/r309_mangos.sql b/src/bindings/scripts/sql/Updates/r309_mangos.sql new file mode 100644 index 00000000000..03bd078c7ab --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r309_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'mob_webbed_creature' WHERE `entry` = 17680; diff --git a/src/bindings/scripts/sql/Updates/r311_mangos.sql b/src/bindings/scripts/sql/Updates/r311_mangos.sql new file mode 100644 index 00000000000..22cb325623b --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r311_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_invis_legion_teleporter' WHERE `entry` = 21807; diff --git a/src/bindings/scripts/sql/Updates/r312_mangos.sql b/src/bindings/scripts/sql/Updates/r312_mangos.sql new file mode 100644 index 00000000000..978152764bd --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r312_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_captured_sunhawk_agent' WHERE `entry` = 17824; +UPDATE `creature_template` SET `ScriptName` = 'npc_neltharaku' WHERE `entry` = 21657; diff --git a/src/bindings/scripts/sql/Updates/r318_mangos.sql b/src/bindings/scripts/sql/Updates/r318_mangos.sql new file mode 100644 index 00000000000..5ce2afca20f --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r318_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npcs_flanis_swiftwing_and_kagrosh' WHERE `entry` IN (21725, 21727); diff --git a/src/bindings/scripts/sql/Updates/r324_mangos.sql b/src/bindings/scripts/sql/Updates/r324_mangos.sql new file mode 100644 index 00000000000..4b9fb91dc2d --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r324_mangos.sql @@ -0,0 +1,3 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_mortog_steamhead' WHERE `entry` = 23373; +UPDATE `creature_template` SET `ScriptName` = 'npc_veronia' WHERE `entry` = 20162; +UPDATE `creature_template` SET `ScriptName` = 'npc_susurrus' WHERE `entry` = 17435; diff --git a/src/bindings/scripts/sql/Updates/r327_mangos.sql b/src/bindings/scripts/sql/Updates/r327_mangos.sql new file mode 100644 index 00000000000..d2d56fdfc3c --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r327_mangos.sql @@ -0,0 +1,5 @@ +-- Sunspring Villagers. +UPDATE `creature_template` SET `ScriptName` = 'mob_sunspring_villager' WHERE `entry` = 18240; + +-- Terestian Illhoof +UPDATE `creature_template` SET `ScriptName` = 'mob_demon_chain' WHERE `entry` = 17248; diff --git a/src/bindings/scripts/sql/Updates/r332_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r332_scriptdev2.sql new file mode 100644 index 00000000000..ea67d3e7392 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r332_scriptdev2.sql @@ -0,0 +1,2 @@ +ALTER TABLE eventai_summons +ADD `comment` varchar(255) NOT NULL default '' COMMENT 'Summon Comment' AFTER spawntimesecs; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r333_mangos.sql b/src/bindings/scripts/sql/Updates/r333_mangos.sql new file mode 100644 index 00000000000..48a7f673fad --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r333_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_oronok_tornheart' WHERE `entry` = 21183; diff --git a/src/bindings/scripts/sql/Updates/r334_mangos.sql b/src/bindings/scripts/sql/Updates/r334_mangos.sql new file mode 100644 index 00000000000..5d010c62209 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r334_mangos.sql @@ -0,0 +1 @@ +UPDATE `instance_template` SET `script` = 'instance_sethekk_halls' WHERE `map` = 556; diff --git a/src/bindings/scripts/sql/Updates/r336_mangos.sql b/src/bindings/scripts/sql/Updates/r336_mangos.sql new file mode 100644 index 00000000000..993b954bb4b --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r336_mangos.sql @@ -0,0 +1,9 @@ +UPDATE `gameobject_template` SET `ScriptName` = 'go_tablet_of_madness' WHERE `entry` = 180368; +UPDATE `creature_template` SET `ScriptName` = 'boss_grilek' WHERE `entry` = 15082; +UPDATE `creature_template` SET `ScriptName` = 'boss_hazzarah' WHERE `entry` = 15083; +UPDATE `creature_template` SET `ScriptName` = 'boss_renataki' WHERE `entry` = 15084; +UPDATE `creature_template` SET `ScriptName` = 'boss_wushoolay' WHERE `entry` = 15085; +UPDATE `creature_template` SET `ScriptName` = 'mobs_zulgurub' WHERE `entry` = 14883; +UPDATE `creature_template` SET `ScriptName` = 'mob_blackwing_lair' WHERE `entry` = 12468; + + diff --git a/src/bindings/scripts/sql/Updates/r352_mangos.sql b/src/bindings/scripts/sql/Updates/r352_mangos.sql new file mode 100644 index 00000000000..a40281bcd33 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r352_mangos.sql @@ -0,0 +1,23 @@ +-- Moroes +update creature_template set scriptname = "boss_moroes" where entry = 15687; +update creature_template set scriptname = "boss_baroness_dorothea_millstipe" where entry = 19875; +update creature_template set scriptname = "boss_baron_rafe_dreuger" where entry = 19874; +update creature_template set scriptname = "boss_lady_catriona_von_indi" where entry = 19872; +update creature_template set scriptname = "boss_lady_keira_berrybuck" where entry = 17007; +update creature_template set scriptname = "boss_lord_robin_daris" where entry = 19876; +update creature_template set scriptname = "boss_lord_crispin_ference" where entry = 19873; + +-- Barnes +update creature_template set scriptname = "npc_barnes" where entry = 16812; +-- Oz Event +update creature_template set scriptname = "boss_dorothee" where entry = 17535; +update creature_template set scriptname = "boss_strawman" where entry = 17543; +update creature_template set scriptname = "boss_tinhead" where entry = 17547; +update creature_template set scriptname = "boss_roar" where entry = 17546; +update creature_template set scriptname = "boss_crone" where entry = 18168; +-- Hood Event +update creature_template set scriptname = "npc_grandmother" where entry = 17603; +update creature_template set scriptname = "boss_bigbadwolf" where entry = 17521; +-- Romeo and Juliet disabled for now +-- update creature_template set scriptname = "boss_julianne" where entry = 17543; +-- update creature_template set scriptname = "boss_romulo" where entry = 17533; diff --git a/src/bindings/scripts/sql/Updates/r355_mangos.sql b/src/bindings/scripts/sql/Updates/r355_mangos.sql new file mode 100644 index 00000000000..a40281bcd33 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r355_mangos.sql @@ -0,0 +1,23 @@ +-- Moroes +update creature_template set scriptname = "boss_moroes" where entry = 15687; +update creature_template set scriptname = "boss_baroness_dorothea_millstipe" where entry = 19875; +update creature_template set scriptname = "boss_baron_rafe_dreuger" where entry = 19874; +update creature_template set scriptname = "boss_lady_catriona_von_indi" where entry = 19872; +update creature_template set scriptname = "boss_lady_keira_berrybuck" where entry = 17007; +update creature_template set scriptname = "boss_lord_robin_daris" where entry = 19876; +update creature_template set scriptname = "boss_lord_crispin_ference" where entry = 19873; + +-- Barnes +update creature_template set scriptname = "npc_barnes" where entry = 16812; +-- Oz Event +update creature_template set scriptname = "boss_dorothee" where entry = 17535; +update creature_template set scriptname = "boss_strawman" where entry = 17543; +update creature_template set scriptname = "boss_tinhead" where entry = 17547; +update creature_template set scriptname = "boss_roar" where entry = 17546; +update creature_template set scriptname = "boss_crone" where entry = 18168; +-- Hood Event +update creature_template set scriptname = "npc_grandmother" where entry = 17603; +update creature_template set scriptname = "boss_bigbadwolf" where entry = 17521; +-- Romeo and Juliet disabled for now +-- update creature_template set scriptname = "boss_julianne" where entry = 17543; +-- update creature_template set scriptname = "boss_romulo" where entry = 17533; diff --git a/src/bindings/scripts/sql/Updates/r358_mangos.sql b/src/bindings/scripts/sql/Updates/r358_mangos.sql new file mode 100644 index 00000000000..01db3736d2e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r358_mangos.sql @@ -0,0 +1 @@ +update creature_template set scriptname = "mob_cyclone" where entry = 18412; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r364_mangos.sql b/src/bindings/scripts/sql/Updates/r364_mangos.sql new file mode 100644 index 00000000000..d893f4d923c --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r364_mangos.sql @@ -0,0 +1,7 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_ayren_cloudbreaker' WHERE `entry` = 25059; +UPDATE `creature_template` SET `ScriptName` = 'npc_unrestrained_dragonhawk' WHERE `entry` = 25236; +UPDATE `creature_template` SET `ScriptName` = 'npc_sputtervalve' WHERE `entry` = 3442; +UPDATE `creature_template` SET `ScriptName` = 'npc_budd_nedreck' WHERE `entry` = 23559; +UPDATE `creature_template` SET `ScriptName` = 'npc_blood_knight_dawnstar' WHERE `entry` = 17832; +UPDATE `creature_template` SET `ScriptName` = 'npc_stone_watcher_of_norgannon' WHERE `entry` = 7918; +UPDATE `creature_template` SET `ScriptName` = 'npc_lore_keeper_of_norgannon' WHERE `entry` = 7172; diff --git a/src/bindings/scripts/sql/Updates/r367_mangos.sql b/src/bindings/scripts/sql/Updates/r367_mangos.sql new file mode 100644 index 00000000000..29a27c7d745 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r367_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'mob_aquementas' WHERE `entry` = 9453; diff --git a/src/bindings/scripts/sql/Updates/r368_mangos.sql b/src/bindings/scripts/sql/Updates/r368_mangos.sql new file mode 100644 index 00000000000..b4cf82da064 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r368_mangos.sql @@ -0,0 +1,5 @@ +UPDATE `instance_template` SET `script` = 'instance_shadowfang_keep' WHERE `map` = 33; +UPDATE `creature_template` SET `ScriptName` = 'boss_fenrus' WHERE `entry` = 4274; +UPDATE `creature_template` SET `ScriptName` = 'boss_nandos' WHERE `entry` = 3927; +UPDATE `creature_template` SET `ScriptName` = 'boss_rethilgore' WHERE `entry` = 3914; +UPDATE `creature_template` SET `ScriptName` = 'npc_shadowfang_prisoner' WHERE `entry` IN (3849, 3850); diff --git a/src/bindings/scripts/sql/Updates/r369_mangos.sql b/src/bindings/scripts/sql/Updates/r369_mangos.sql new file mode 100644 index 00000000000..1d053e5f583 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r369_mangos.sql @@ -0,0 +1,5 @@ +UPDATE `creature_template` SET `ScriptName` = 'boss_rage_winterchill' WHERE `entry` = 17767; +UPDATE `creature_template` SET `ScriptName` = 'boss_anetheron' WHERE `entry` = 17808; +UPDATE `creature_template` SET `ScriptName` = 'boss_kazrogal' WHERE `entry` = 17888; +UPDATE `creature_template` SET `ScriptName` = 'boss_azgalor' WHERE `entry` = 17842; +UPDATE `creature_template` SET `ScriptName` = 'mob_echo_of_archimonde' WHERE `entry` = 13083; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r374_mangos.sql b/src/bindings/scripts/sql/Updates/r374_mangos.sql new file mode 100644 index 00000000000..f430c086f00 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r374_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_the_scourge_cauldron' WHERE `entry` = 11152; diff --git a/src/bindings/scripts/sql/Updates/r386_mangos.sql b/src/bindings/scripts/sql/Updates/r386_mangos.sql new file mode 100644 index 00000000000..7913666b5ce --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r386_mangos.sql @@ -0,0 +1,7 @@ +UPDATE `item_template` SET `ScriptName` = 'item_Sparrowhawk_Net' WHERE `entry` = 32321; +UPDATE `item_template` SET `ScriptName` = 'item_Blackwhelp_Net' WHERE `entry` = 31129; + +UPDATE `creature_template` SET `ScriptName` = 'npc_spirit_of_olum' WHERE `entry` = 23411; + +UPDATE creature_template SET ScriptName='boss_warp_splinter' WHERE entry='17977'; +UPDATE creature_template SET ScriptName='mob_warp_splinter_treant' WHERE entry='19949'; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r417_mangos.sql b/src/bindings/scripts/sql/Updates/r417_mangos.sql new file mode 100644 index 00000000000..c9c08fe598b --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r417_mangos.sql @@ -0,0 +1 @@ +UPDATE `item_template` SET `ScriptName` = 'item_tame_beast_rods' WHERE `entry` IN (15908,15911,15913,15914,15915,15916,15917,15919,15920,15921,15922,15923,23697,23702,23703,23896,23897,23898); diff --git a/src/bindings/scripts/sql/Updates/r428_mangos.sql b/src/bindings/scripts/sql/Updates/r428_mangos.sql new file mode 100644 index 00000000000..997c636332d --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r428_mangos.sql @@ -0,0 +1,4 @@ +-- Quest Support 10804 (Kindness) +UPDATE creature_template SET scriptname = 'mob_mature_netherwing_drake' WHERE entry = 21648; +-- Quest Support 10854 (The Force of Neltharaku) +UPDATE creature_template SET scriptname = 'mob_enslaved_netherwing_drake' WHERE entry = 21722; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r431_mangos.sql b/src/bindings/scripts/sql/Updates/r431_mangos.sql new file mode 100644 index 00000000000..bd8481815eb --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r431_mangos.sql @@ -0,0 +1,4 @@ +UPDATE `creature_template` SET `ScriptName`='npc_zephyr' WHERE `entry`=25967; +UPDATE `instance_template` SET `script`='instance_old_hillsbrad' WHERE `map`=560; +UPDATE `creature_template` SET `ScriptName`='npc_brazen' WHERE `entry`=18725; +UPDATE `creature_template` SET `ScriptName`='npc_erozion' WHERE `entry`=18723; diff --git a/src/bindings/scripts/sql/Updates/r444_mangos.sql b/src/bindings/scripts/sql/Updates/r444_mangos.sql new file mode 100644 index 00000000000..c45fa7b9620 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r444_mangos.sql @@ -0,0 +1,5 @@ +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry`=22120; +UPDATE `creature_template` SET `ScriptName`='mob_fathom_sporebat' WHERE `entry`=22140; +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry`=20318; +UPDATE `creature_template` SET `ScriptName`='mob_eventai' WHERE `entry` IN (3927,3914,4274); +UPDATE `creature_template` SET `ScriptName`='mob_eventai' WHERE `entry` IN (21717,21718,21719,21720,22331,21878,21879,18371,18343,13083,17808); diff --git a/src/bindings/scripts/sql/Updates/r445_mangos.sql b/src/bindings/scripts/sql/Updates/r445_mangos.sql new file mode 100644 index 00000000000..5ca5011dd4e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r445_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName`='boss_high_astromancer_solarian' WHERE `entry`=18805; +UPDATE `creature_template` SET `ScriptName`='mob_solarium_priest' WHERE `entry`=18806; diff --git a/src/bindings/scripts/sql/Updates/r446_mangos.sql b/src/bindings/scripts/sql/Updates/r446_mangos.sql new file mode 100644 index 00000000000..a8a819e3b67 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r446_mangos.sql @@ -0,0 +1,2 @@ +-- Set ACID (mob_eventai) ScriptName for 4 Hyjal-wave bosses +UPDATE `creature_template` SET `ScriptName` = 'mob_eventai' WHERE `entry` IN (17767, 17808, 17888, 17842); diff --git a/src/bindings/scripts/sql/Updates/r448_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r448_scriptdev2.sql new file mode 100644 index 00000000000..66d78082aa0 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r448_scriptdev2.sql @@ -0,0 +1,5 @@ +DROP TABLE IF EXISTS `db_version`; +DROP TABLE IF EXISTS `sd2_db_version`; +CREATE TABLE `sd2_db_version` ( +`version` varchar(255) NOT NULL default '' COMMENT 'Database version string' +) ENGINE=MyISAM DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r462_mangos.sql b/src/bindings/scripts/sql/Updates/r462_mangos.sql new file mode 100644 index 00000000000..6d8ac269de3 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r462_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName`='npc_taretha' WHERE `entry`=18887; +UPDATE `creature_template` SET `ScriptName`='npc_thrall_old_hillsbrad' WHERE `entry`=17876; diff --git a/src/bindings/scripts/sql/Updates/r465_mangos.sql b/src/bindings/scripts/sql/Updates/r465_mangos.sql new file mode 100644 index 00000000000..35d7d3834f1 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r465_mangos.sql @@ -0,0 +1,3 @@ +UPDATE `creature_template` SET `ScriptName`='boss_harbinger_skyriss' WHERE `entry`=20912; +UPDATE `creature_template` SET `ScriptName`='npc_warden_mellichar' WHERE `entry`=20904; +UPDATE `creature_template` SET `ScriptName`='npc_millhouse_manastorm' WHERE `entry`=20977; diff --git a/src/bindings/scripts/sql/Updates/r467_mangos.sql b/src/bindings/scripts/sql/Updates/r467_mangos.sql new file mode 100644 index 00000000000..711bba7cc94 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r467_mangos.sql @@ -0,0 +1 @@ +UPDATE `item_template` SET `ScriptName`='item_flying_machine' WHERE `entry` IN (34060,34061); diff --git a/src/bindings/scripts/sql/Updates/r473_mangos.sql b/src/bindings/scripts/sql/Updates/r473_mangos.sql new file mode 100644 index 00000000000..f9918bc1b71 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r473_mangos.sql @@ -0,0 +1,3 @@ +UPDATE `item_template` SET `ScriptName`='item_soul_cannon' WHERE `entry`=32825; +UPDATE `item_template` SET `ScriptName`='item_sparrowhawk_net' WHERE `entry`=32321; +UPDATE `item_template` SET `ScriptName`='item_blackwhelp_net' WHERE `entry`=31129; diff --git a/src/bindings/scripts/sql/Updates/r476_mangos.sql b/src/bindings/scripts/sql/Updates/r476_mangos.sql new file mode 100644 index 00000000000..998045f2193 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r476_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_engineer_spark_overgrind' WHERE `entry`=17243; diff --git a/src/bindings/scripts/sql/Updates/r477_mangos.sql b/src/bindings/scripts/sql/Updates/r477_mangos.sql new file mode 100644 index 00000000000..0f9c76c5336 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r477_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='' WHERE `entry`=19577; diff --git a/src/bindings/scripts/sql/Updates/r479_mangos.sql b/src/bindings/scripts/sql/Updates/r479_mangos.sql new file mode 100644 index 00000000000..216fe068317 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r479_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_chicken_cluck' WHERE `entry`=620; diff --git a/src/bindings/scripts/sql/Updates/r482_mangos.sql b/src/bindings/scripts/sql/Updates/r482_mangos.sql new file mode 100644 index 00000000000..b431b85c90e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r482_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_dancing_flames' WHERE `entry`=25305; diff --git a/src/bindings/scripts/sql/Updates/r484_mangos.sql b/src/bindings/scripts/sql/Updates/r484_mangos.sql new file mode 100644 index 00000000000..1f4fefd7fa1 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r484_mangos.sql @@ -0,0 +1,8 @@ +-- Quest support 9678 +UPDATE `gameobject_template` SET `ScriptName`='go_gilded_brazier' WHERE `entry` = '181956'; + +-- Archimonde with related creatures. +UPDATE `creature_template` SET `Scriptname` = "boss_archimonde" WHERE `entry` = 17968; +UPDATE `creature_template` SET `ScriptName` = "mob_doomfire" WHERE `entry` = 18095; +UPDATE `creature_template` SET `Scriptname` = "mob_doomfire_targetting" WHERE `entry` = 18104; +UPDATE `creature_template` SET `ScriptName` = "mob_ancient_wisp" WHERE `entry` = 17946; diff --git a/src/bindings/scripts/sql/Updates/r486_mangos.sql b/src/bindings/scripts/sql/Updates/r486_mangos.sql new file mode 100644 index 00000000000..28920abf0db --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r486_mangos.sql @@ -0,0 +1 @@ +UPDATE `gameobject_template` SET `ScriptName`='' WHERE `entry`=181956; diff --git a/src/bindings/scripts/sql/Updates/r487_mangos.sql b/src/bindings/scripts/sql/Updates/r487_mangos.sql new file mode 100644 index 00000000000..229be423977 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r487_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_prospector_anvilward' WHERE `entry`=15420; diff --git a/src/bindings/scripts/sql/Updates/r494_mangos.sql b/src/bindings/scripts/sql/Updates/r494_mangos.sql new file mode 100644 index 00000000000..c8f5aef5d9a --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r494_mangos.sql @@ -0,0 +1 @@ +UPDATE `gameobject_template` SET `ScriptName` = 'gameobject_cage_trap' WHERE `entry` = 185916; diff --git a/src/bindings/scripts/sql/Updates/r501_mangos.sql b/src/bindings/scripts/sql/Updates/r501_mangos.sql new file mode 100644 index 00000000000..7242f3b8a7b --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r501_mangos.sql @@ -0,0 +1,4 @@ +UPDATE `creature_template` SET `ScriptName`='npc_prof_alchemy' WHERE `entry` IN (17909,19052,22427); +UPDATE `creature_template` SET `ScriptName`='npc_prof_blacksmith' WHERE `entry` IN (5164,11145,11146,11176,11177,11178,11191,11192,11193); +UPDATE `creature_template` SET `ScriptName`='npc_prof_leather' WHERE `entry` IN (7866,7867,7868,7869,7870,7871); +UPDATE `creature_template` SET `ScriptName`='npc_prof_tailor' WHERE `entry` IN (22208,22212,22213); diff --git a/src/bindings/scripts/sql/Updates/r513_mangos.sql b/src/bindings/scripts/sql/Updates/r513_mangos.sql new file mode 100644 index 00000000000..fcc3433fa59 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r513_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_salsalabim' WHERE `entry`=18584; diff --git a/src/bindings/scripts/sql/Updates/r514_mangos.sql b/src/bindings/scripts/sql/Updates/r514_mangos.sql new file mode 100644 index 00000000000..108948250d9 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r514_mangos.sql @@ -0,0 +1,3 @@ +UPDATE `creature_template` SET `ScriptName`='npc_raliq_the_drunk' WHERE `entry`=18585; +UPDATE `creature_template` SET `ScriptName`='npc_cooshcoosh' WHERE `entry`=18586; +UPDATE `creature_template` SET `ScriptName`='npc_floon' WHERE `entry`=18588; diff --git a/src/bindings/scripts/sql/Updates/r515_mangos.sql b/src/bindings/scripts/sql/Updates/r515_mangos.sql new file mode 100644 index 00000000000..8860641d493 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r515_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_murkblood_overseer' WHERE `entry`=23309; diff --git a/src/bindings/scripts/sql/Updates/r516_mangos.sql b/src/bindings/scripts/sql/Updates/r516_mangos.sql new file mode 100644 index 00000000000..76bd7b83780 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r516_mangos.sql @@ -0,0 +1,3 @@ +UPDATE `creature_template` SET `ScriptName`='npc_neeru_fireblade' WHERE `entry`=3216; +UPDATE `creature_template` SET `ScriptName`='npc_shenthul' WHERE `entry`=3401; +UPDATE `creature_template` SET `ScriptName`='npc_thrall_warchief' WHERE `entry`=4949; diff --git a/src/bindings/scripts/sql/Updates/r517_mangos.sql b/src/bindings/scripts/sql/Updates/r517_mangos.sql new file mode 100644 index 00000000000..2077e416c81 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r517_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_taskmaster_fizzule' WHERE `entry`=7233; diff --git a/src/bindings/scripts/sql/Updates/r518_mangos.sql b/src/bindings/scripts/sql/Updates/r518_mangos.sql new file mode 100644 index 00000000000..5930aba9256 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r518_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_rathis_tomber' WHERE `entry`=16224; diff --git a/src/bindings/scripts/sql/Updates/r519_mangos.sql b/src/bindings/scripts/sql/Updates/r519_mangos.sql new file mode 100644 index 00000000000..fa408a043ec --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r519_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_calvin_montague' WHERE `entry`=6784; diff --git a/src/bindings/scripts/sql/Updates/r520_mangos.sql b/src/bindings/scripts/sql/Updates/r520_mangos.sql new file mode 100644 index 00000000000..959ac79be55 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r520_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `item_template` SET `ScriptName`='item_disciplinary_rod' WHERE `entry`=22473; +UPDATE `item_template` SET `ScriptName`='item_protovoltaic_magneto_collector' WHERE `entry`=30656; diff --git a/src/bindings/scripts/sql/Updates/r521_mangos.sql b/src/bindings/scripts/sql/Updates/r521_mangos.sql new file mode 100644 index 00000000000..37d09546b0a --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r521_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_gregan_brewspewer' WHERE `entry`=7775; diff --git a/src/bindings/scripts/sql/Updates/r522_mangos.sql b/src/bindings/scripts/sql/Updates/r522_mangos.sql new file mode 100644 index 00000000000..159e6e83826 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r522_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName`='guard_shattrath_aldor' WHERE `entry`=18549; +UPDATE `creature_template` SET `ScriptName`='guard_shattrath_scryer' WHERE `entry`=18568; diff --git a/src/bindings/scripts/sql/Updates/r526_mangos.sql b/src/bindings/scripts/sql/Updates/r526_mangos.sql new file mode 100644 index 00000000000..2171bed6570 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r526_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_injured_draenei' WHERE `entry`=16971; diff --git a/src/bindings/scripts/sql/Updates/r528_mangos.sql b/src/bindings/scripts/sql/Updates/r528_mangos.sql new file mode 100644 index 00000000000..90b63aabe40 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r528_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName`='npc_skorn_whitecloud' WHERE `entry`=3052; +UPDATE `creature_template` SET `ScriptName`='npc_blood_knight_stillblade' WHERE `entry`=17768; diff --git a/src/bindings/scripts/sql/Updates/r533_mangos.sql b/src/bindings/scripts/sql/Updates/r533_mangos.sql new file mode 100644 index 00000000000..a05d4bfb732 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r533_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_augustus_the_touched' WHERE `entry`=12384; diff --git a/src/bindings/scripts/sql/Updates/r538_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r538_scriptdev2.sql new file mode 100644 index 00000000000..92ef9ea24ff --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r538_scriptdev2.sql @@ -0,0 +1,60 @@ +-- Structure updates + +ALTER TABLE eventai_scripts +ADD event_flags tinyint(3) unsigned NOT NULL default '0' AFTER event_chance; + +ALTER TABLE eventai_scripts +ADD event_param4 int(11) signed NOT NULL default '0' AFTER event_param3; + +-- Event updates + +-- EVENT_T_TIMER_REPEAT +UPDATE eventai_scripts SET event_param4 = event_param3+event_param1, event_param3 = event_param1, event_param1 = event_param2, event_flags = 1 WHERE event_type = 0; + +-- EVENT_T_TIMER_SINGLE +UPDATE eventai_scripts SET event_param4 = event_param3+event_param1, event_param3 = event_param1, event_param1 = event_param2, event_type = 0 WHERE event_type = 1; + +-- EVENT_T_TIMER_OOC_REPEAT +UPDATE eventai_scripts SET event_param4 = event_param3+event_param1, event_param3 = event_param1, event_param1 = event_param2, event_flags = 1, event_type = 1 WHERE event_type = 2; + +-- EVENT_T_TIMER_OOC_SINGLE +UPDATE eventai_scripts SET event_param4 = event_param3+event_param1, event_param3 = event_param1, event_param1 = event_param2, event_type = 1 WHERE event_type = 3; + +-- EVENT_T_HP_SINGLE +UPDATE eventai_scripts SET event_param4 = event_param3, event_flags = 0, event_type = 2 WHERE event_type = 4; + +-- EVENT_T_MANA_SINGLE +UPDATE eventai_scripts SET event_param4 = event_param3, event_flags = 0, event_type = 3 WHERE event_type = 5; + +-- EVENT_T_AGGRO +UPDATE eventai_scripts SET event_type = 4 WHERE event_type = 6; + +-- EVENT_T_KILL +UPDATE eventai_scripts SET event_param2 = event_param1, event_flags = 1, event_type = 5 WHERE event_type = 7; + +-- EVENT_T_DEATH +UPDATE eventai_scripts SET event_type = 6 WHERE event_type = 8; + +-- EVENT_T_EVADE +UPDATE eventai_scripts SET event_type = 7 WHERE event_type = 9; + +-- EVENT_T_SPELLHIT +UPDATE eventai_scripts SET event_param4 = event_param3, event_flags = 1, event_type = 8 WHERE event_type = 10; + +-- EVENT_T_RANGE +UPDATE eventai_scripts SET event_param4 = event_param3, event_flags = 1, event_type = 9 WHERE event_type = 11; + +-- EVENT_T_OOC_LOS +UPDATE eventai_scripts SET event_param4 = event_param3, event_flags = 1, event_type = 10 WHERE event_type = 12; + +-- EVENT_T_SPAWNED +UPDATE eventai_scripts SET event_type = 11 WHERE event_type = 13; + +-- EVENT_T_TARGET_HP +UPDATE eventai_scripts SET event_param4 = event_param3, event_flags = 1, event_type = 12 WHERE event_type = 14; + +-- EVENT_T_TARGET_CASTING +UPDATE eventai_scripts SET event_param2 = event_param1, event_flags = 1, event_type = 13 WHERE event_type = 15; + +-- EVENT_T_FRIENDLY_HP +UPDATE eventai_scripts SET event_param4 = event_param3, event_flags = 1, event_type = 14 WHERE event_type = 16; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r547_mangos.sql b/src/bindings/scripts/sql/Updates/r547_mangos.sql new file mode 100644 index 00000000000..cc98862022e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r547_mangos.sql @@ -0,0 +1,22 @@ +/* Magister's Terrace */ +UPDATE `instance_template` SET `script` = 'instance_magisters_terrace' WHERE `map` = 585; +UPDATE `creature_template` SET `ScriptName` = 'boss_selin_fireheart' WHERE `entry` = 24723; +UPDATE `creature_template` SET `ScriptName` = 'mob_fel_crystal' WHERE `entry` = 24722; +UPDATE `creature_template` SET `ScriptName` = 'boss_vexallus' WHERE `entry` = 24744; +UPDATE `creature_template` SET `ScriptName` = 'mob_pure_energy' WHERE `entry` = 24745; +UPDATE `creature_template` SET `ScriptName` = 'boss_priestess_delrissa' WHERE `entry` = 24560; +UPDATE `creature_template` SET `ScriptName` = 'boss_kagani_nightstrike' WHERE `entry` = 24557; +UPDATE `creature_template` SET `ScriptName` = 'boss_ellris_duskhallow' WHERE `entry` = 24558; +UPDATE `creature_template` SET `ScriptName` = 'mob_imp' WHERE `entry` = 24656; +UPDATE `creature_template` SET `ScriptName` = 'boss_eramas_brightblaze' WHERE `entry` = 24554; +UPDATE `creature_template` SET `ScriptName` = 'boss_yazzai' WHERE `entry` = 24561; +UPDATE `creature_template` SET `ScriptName` = 'boss_warlord_salaris' WHERE `entry` = 24559; +UPDATE `creature_template` SET `ScriptName` = 'boss_garaxxas' WHERE `entry` = 24555; +UPDATE `creature_template` SET `ScriptName` = 'mob_sliver' WHERE `entry` = 24552; +UPDATE `creature_template` SET `ScriptName` = 'boss_apoko' WHERE `entry` = 24553; +UPDATE `creature_template` SET `ScriptName` = 'boss_zelfan' WHERE `entry` = 24556; +UPDATE `creature_template` SET `ScriptName` = 'boss_felblood_kaelthas' WHERE `entry` = 24664; +UPDATE `creature_template` SET `ScriptName` = 'mob_arcane_sphere' WHERE `entry` = 24708; +UPDATE `creature_template` SET `ScriptName` = 'mob_felkael_phoenix' WHERE `entry` = 24674; +UPDATE `creature_template` SET `ScriptName` = 'mob_felkael_phoenix_egg' WHERE `entry` = 24675; +UPDATE `creature_template` SET `ScriptName` = 'mob_felkael_flamestrike' WHERE `entry` = 24666; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r554_mangos.sql b/src/bindings/scripts/sql/Updates/r554_mangos.sql new file mode 100644 index 00000000000..b6beac3adce --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r554_mangos.sql @@ -0,0 +1,3 @@ +UPDATE `creature_template` SET `ScriptName`='boss_mekgineer_steamrigger' WHERE `entry`=17796; +UPDATE `creature_template` SET `ScriptName`='mob_steamrigger_mechanic' WHERE `entry`=17951; +UPDATE `creature_template` SET `ScriptName`='mob_naga_distiller' WHERE `entry`=17954; diff --git a/src/bindings/scripts/sql/Updates/r555_mangos.sql b/src/bindings/scripts/sql/Updates/r555_mangos.sql new file mode 100644 index 00000000000..f0284ddd037 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r555_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `gameobject_template` SET `ScriptName`='go_manaforge_control_console' WHERE `entry` IN (183770,183956,184311,184312); +UPDATE `creature_template` SET `ScriptName`='npc_manaforge_control_console' WHERE `entry` IN (20209,20417,20418,20440); diff --git a/src/bindings/scripts/sql/Updates/r56.sql b/src/bindings/scripts/sql/Updates/r56.sql new file mode 100644 index 00000000000..69e31e9e21c --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r56.sql @@ -0,0 +1,2 @@ +/* Remove guildmaster script from npc's. Core has full support */ +UPDATE `creature_template` SET `ScriptName` = '' WHERE `entry` IN (4974,5054,4613); diff --git a/src/bindings/scripts/sql/Updates/r575_mangos.sql b/src/bindings/scripts/sql/Updates/r575_mangos.sql new file mode 100644 index 00000000000..bbf52bd5c96 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r575_mangos.sql @@ -0,0 +1 @@ +UPDATE `item_template` SET `ScriptName`='item_gor_dreks_ointment' WHERE `entry`=30175; diff --git a/src/bindings/scripts/sql/Updates/r576_mangos.sql b/src/bindings/scripts/sql/Updates/r576_mangos.sql new file mode 100644 index 00000000000..0df34443309 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r576_mangos.sql @@ -0,0 +1 @@ +UPDATE `item_template` SET `ScriptName`='item_muiseks_vessel' WHERE `entry` IN (9606,9618,9619,9620,9621); diff --git a/src/bindings/scripts/sql/Updates/r578_mangos.sql b/src/bindings/scripts/sql/Updates/r578_mangos.sql new file mode 100644 index 00000000000..f82ea62c26e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r578_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_forest_frog' WHERE `entry`=24396; diff --git a/src/bindings/scripts/sql/Updates/r584_mangos.sql b/src/bindings/scripts/sql/Updates/r584_mangos.sql new file mode 100644 index 00000000000..0980b86cc4e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r584_mangos.sql @@ -0,0 +1 @@ +UPDATE `item_template` SET `ScriptName`='item_voodoo_charm' WHERE `entry`=8149; diff --git a/src/bindings/scripts/sql/Updates/r59.sql b/src/bindings/scripts/sql/Updates/r59.sql new file mode 100644 index 00000000000..219f96220b1 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r59.sql @@ -0,0 +1,3 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_narm_faulk' WHERE `entry`= 6177; +UPDATE `creature_template` SET `ScriptName` = 'npc_q8304' WHERE `entry` IN (15170, 15171); +UPDATE `creature_template` SET `ScriptName` = 'npc_q8507_q8731' WHERE `entry` IN (15440, 15612); \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r590_mangos.sql b/src/bindings/scripts/sql/Updates/r590_mangos.sql new file mode 100644 index 00000000000..72348756d91 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r590_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName`='npc_sergeant_bly' WHERE `entry`=7604; +UPDATE `creature_template` SET `ScriptName`='npc_weegli_blastfuse' WHERE `entry`=7607; diff --git a/src/bindings/scripts/sql/Updates/r591_mangos.sql b/src/bindings/scripts/sql/Updates/r591_mangos.sql new file mode 100644 index 00000000000..253fe3ed53b --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r591_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='mobs_nether_drake' WHERE `entry` IN (20021,21817,21820,21821,21823); diff --git a/src/bindings/scripts/sql/Updates/r593_mangos.sql b/src/bindings/scripts/sql/Updates/r593_mangos.sql new file mode 100644 index 00000000000..ec6730e3d4e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r593_mangos.sql @@ -0,0 +1 @@ +UPDATE `gameobject_template` SET `ScriptName`='go_tablet_of_the_seven' WHERE `entry`=169294; diff --git a/src/bindings/scripts/sql/Updates/r594_mangos.sql b/src/bindings/scripts/sql/Updates/r594_mangos.sql new file mode 100644 index 00000000000..3315fd47241 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r594_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='mob_anubisath_sentinel' WHERE `entry`=15264; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r596_mangos.sql b/src/bindings/scripts/sql/Updates/r596_mangos.sql new file mode 100644 index 00000000000..2f0e4587883 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r596_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='boss_ambassador_hellmaw' WHERE `entry`=18731; diff --git a/src/bindings/scripts/sql/Updates/r610_mangos.sql b/src/bindings/scripts/sql/Updates/r610_mangos.sql new file mode 100644 index 00000000000..1aa9f4b50b4 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r610_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName`='npc_lady_sylvanas_windrunner' WHERE `entry`=10181; +UPDATE `creature_template` SET `ScriptName`='npc_highborne_lamenter' WHERE `entry`=21628; diff --git a/src/bindings/scripts/sql/Updates/r615_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r615_scriptdev2.sql new file mode 100644 index 00000000000..5c5625860f8 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r615_scriptdev2.sql @@ -0,0 +1,4 @@ +-- Structure updates + +ALTER TABLE localized_texts +ADD `locale_8` varchar(255) NOT NULL default '' AFTER locale_7; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r617_mangos.sql b/src/bindings/scripts/sql/Updates/r617_mangos.sql new file mode 100644 index 00000000000..7a30f9c553d --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r617_mangos.sql @@ -0,0 +1 @@ +UPDATE `item_template` SET `ScriptName`='item_razorthorn_flayer_gland' WHERE `entry`=34255; diff --git a/src/bindings/scripts/sql/Updates/r621_mangos.sql b/src/bindings/scripts/sql/Updates/r621_mangos.sql new file mode 100644 index 00000000000..822de701ebb --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r621_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='boss_talon_king_ikiss' WHERE `entry`=18473; diff --git a/src/bindings/scripts/sql/Updates/r628_mangos.sql b/src/bindings/scripts/sql/Updates/r628_mangos.sql new file mode 100644 index 00000000000..47d286d2718 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r628_mangos.sql @@ -0,0 +1,3 @@ +UPDATE `instance_template` SET `script`='instance_shattered_halls' WHERE `map`=540; +UPDATE `creature_template` SET `ScriptName`='mob_fel_orc_convert' WHERE `entry`=17083; +UPDATE `creature_template` SET `ScriptName`='mob_lesser_shadow_fissure' WHERE `entry`=17471; diff --git a/src/bindings/scripts/sql/Updates/r63.sql b/src/bindings/scripts/sql/Updates/r63.sql new file mode 100644 index 00000000000..4627d40067b --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r63.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_bartleby' WHERE `entry` IN (6090); \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r632_mangos.sql b/src/bindings/scripts/sql/Updates/r632_mangos.sql new file mode 100644 index 00000000000..4ea0c35e6ea --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r632_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName`='boss_warbringer_omrogg' WHERE `entry`=16809; +UPDATE `creature_template` SET `ScriptName`='mob_omrogg_heads' WHERE `entry` IN (19523,19524); diff --git a/src/bindings/scripts/sql/Updates/r633_mangos.sql b/src/bindings/scripts/sql/Updates/r633_mangos.sql new file mode 100644 index 00000000000..bbf75d1f961 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r633_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_rogue_trainer' WHERE `entry` IN (918,4163,3328,4583,5165,5167,13283,16684); diff --git a/src/bindings/scripts/sql/Updates/r634_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r634_scriptdev2.sql new file mode 100644 index 00000000000..31791a57084 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r634_scriptdev2.sql @@ -0,0 +1,35 @@ +DROP TABLE IF EXISTS `script_texts`; +CREATE TABLE `script_texts` ( +`ScriptName` varchar(255) NOT NULL default '', +`Id` int(11) unsigned NOT NULL default '0', +`Sound` int(11) unsigned NOT NULL default '0', +`Type` int(11) unsigned NOT NULL default '0', +`Language` int(11) unsigned NOT NULL default '0', +`Text` varchar(255) NOT NULL default '', +`Comment` varchar(255) NOT NULL default '', +PRIMARY KEY (`ScriptName`,`Id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Script Texts'; + + +DROP TABLE IF EXISTS `script_localized_texts`; +CREATE TABLE `script_localized_texts` ( +`id` int(11) unsigned NOT NULL auto_increment COMMENT 'Identifier', +`locale_1` varchar(255) NOT NULL default '', +`locale_2` varchar(255) NOT NULL default '', +`locale_3` varchar(255) NOT NULL default '', +`locale_4` varchar(255) NOT NULL default '', +`locale_5` varchar(255) NOT NULL default '', +`locale_6` varchar(255) NOT NULL default '', +`locale_7` varchar(255) NOT NULL default '', +`locale_8` varchar(255) NOT NULL default '', +`comment` varchar(255) NOT NULL default '' COMMENT 'Text Comment', +PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Localized Script Text'; + + +DROP TABLE IF EXISTS `eventai_texts`; +CREATE TABLE `eventai_texts` AS SELECT `id`, `locale_0` AS `text`, `comment` FROM `localized_texts`; +TRUNCATE TABLE `localized_texts`; +DROP TABLE IF EXISTS `eventai_localized_texts`; +ALTER TABLE `localized_texts` RENAME TO `eventai_localized_texts`; +ALTER TABLE `eventai_localized_texts` DROP COLUMN `locale_0`; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r636_mangos.sql b/src/bindings/scripts/sql/Updates/r636_mangos.sql new file mode 100644 index 00000000000..55eea42bf2a --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r636_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='boss_laj' WHERE `entry`=17980; diff --git a/src/bindings/scripts/sql/Updates/r637_mangos.sql b/src/bindings/scripts/sql/Updates/r637_mangos.sql new file mode 100644 index 00000000000..03a53749cab --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r637_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='boss_high_botanist_freywinn' WHERE `entry`=17975; diff --git a/src/bindings/scripts/sql/Updates/r638_mangos.sql b/src/bindings/scripts/sql/Updates/r638_mangos.sql new file mode 100644 index 00000000000..cf20d33e957 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r638_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `item_template` SET `ScriptName`='item_attuned_crystal_cores' WHERE `entry`=34368; +UPDATE `creature_template` SET `ScriptName`='npc_converted_sentry' WHERE `entry`=24981; diff --git a/src/bindings/scripts/sql/Updates/r639_mangos.sql b/src/bindings/scripts/sql/Updates/r639_mangos.sql new file mode 100644 index 00000000000..7946ac237ed --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r639_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_ravenholdt' WHERE `entry`=13936; diff --git a/src/bindings/scripts/sql/Updates/r642_mangos.sql b/src/bindings/scripts/sql/Updates/r642_mangos.sql new file mode 100644 index 00000000000..b9963728279 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r642_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='mob_shadowmoon_channeler' WHERE `entry`=17653; diff --git a/src/bindings/scripts/sql/Updates/r643_mangos.sql b/src/bindings/scripts/sql/Updates/r643_mangos.sql new file mode 100644 index 00000000000..8659ebcb850 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r643_mangos.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName`='mob_stolen_soul' WHERE `entry`=18441; +UPDATE `creature_template` SET `ScriptName`='mob_avatar_of_martyred' WHERE `entry`=18478; diff --git a/src/bindings/scripts/sql/Updates/r646_mangos.sql b/src/bindings/scripts/sql/Updates/r646_mangos.sql new file mode 100644 index 00000000000..3f43d49bc32 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r646_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='boss_harbinger_skyriss_illusion' WHERE `entry` IN (21466,21467); diff --git a/src/bindings/scripts/sql/Updates/r647_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r647_scriptdev2.sql new file mode 100644 index 00000000000..bd259718b81 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r647_scriptdev2.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS `script_texts`; +CREATE TABLE `script_texts` ( +`id` int(11) unsigned NOT NULL auto_increment COMMENT 'Identifier', +`sound` int(11) unsigned NOT NULL default '0', +`type` int(11) unsigned NOT NULL default '0', +`language` int(11) unsigned NOT NULL default '0', +`text` varchar(255) NOT NULL default '', +`comment` varchar(255) NOT NULL default '', +PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Script Texts'; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r65.sql b/src/bindings/scripts/sql/Updates/r65.sql new file mode 100644 index 00000000000..2c878bfed56 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r65.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `npcflag`='3', `ScriptName`='npc_lothos_riftwalker' WHERE `entry`='14387'; +UPDATE creature_template SET ScriptName="boss_gruul" WHERE entry=19044; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r656_scriptdev2.sql b/src/bindings/scripts/sql/Updates/r656_scriptdev2.sql new file mode 100644 index 00000000000..96fcd146d76 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r656_scriptdev2.sql @@ -0,0 +1,5 @@ +ALTER TABLE +`eventai_localized_texts` ADD COLUMN `locale_8` varchar(255) NOT NULL default '' AFTER locale_7; + +ALTER TABLE +`script_localized_texts` ADD COLUMN `locale_8` varchar(255) NOT NULL default '' AFTER locale_7; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r658_mangos.sql b/src/bindings/scripts/sql/Updates/r658_mangos.sql new file mode 100644 index 00000000000..50ae5fcbd4c --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r658_mangos.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName`='npc_custodian_of_time' WHERE `entry`=20129; diff --git a/src/bindings/scripts/sql/Updates/r659_mangos.sql b/src/bindings/scripts/sql/Updates/r659_mangos.sql new file mode 100644 index 00000000000..feb9379b8af --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r659_mangos.sql @@ -0,0 +1,4 @@ +UPDATE `creature_template` SET `ScriptName`='npc_akama_shade' WHERE `entry`=22990; -- Akama at Shade of Akama +UPDATE `creature_template` SET `ScriptName`='npc_akama_illidan' WHERE `entry`=23089; -- Akama at Illidan +UPDATE `creature_template` SET `ScriptName`='mob_illidari_council' WHERE `entry`=23426; -- Illidari Council controller mob +UPDATE `creature_template` SET `ScriptName`='mob_blood_elf_council_voice_trigger' WHERE `entry` = 23499; -- Voice Trigger Mob (Controls Aggro + Enrage yells) diff --git a/src/bindings/scripts/sql/Updates/r72.sql b/src/bindings/scripts/sql/Updates/r72.sql new file mode 100644 index 00000000000..fd79e95cd66 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r72.sql @@ -0,0 +1,4 @@ +UPDATE `creature_template` SET `ScriptName` = 'mobs_mana_tapped' WHERE `entry` IN (15273,15274,15294,15298,15367); +UPDATE `creature_template` SET `ScriptName` = 'mobs_ghoul_flayer' WHERE `entry` IN (8530, 8531, 8532); +UPDATE `creature_template` SET `ScriptName` = 'mobs_bonechewer_orc' WHERE `entry` IN (16876, 16925, 18952, 19701); +UPDATE `item_template` SET `ScriptName` = 'purification_mixture' WHERE `entry`= 23268; \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r78.sql b/src/bindings/scripts/sql/Updates/r78.sql new file mode 100644 index 00000000000..783482aed8e --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r78.sql @@ -0,0 +1,3 @@ +UPDATE `item_template` SET `ScriptName` = 'nether_wraith_beacon' WHERE `entry` = 31742; +UPDATE `item_template` SET `ScriptName` = 'area_52_special' WHERE `entry` = 28132; +UPDATE `creature_template` SET `ScriptName` = 'guard_shattrath' WHERE `entry` = 19687; diff --git a/src/bindings/scripts/sql/Updates/r81.sql b/src/bindings/scripts/sql/Updates/r81.sql new file mode 100644 index 00000000000..c1785f088d8 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r81.sql @@ -0,0 +1,35 @@ +UPDATE `creature_template` SET `ScriptName` = 'boss_gyth' WHERE `entry` = 10339; +UPDATE `creature_template` SET `ScriptName` = 'boss_rend_blackhand' WHERE `entry` = 10429; +UPDATE `creature_template` SET `ScriptName` = 'boss_pyroguard_emberseer' WHERE `entry` = 9816; +UPDATE `creature_template` SET `ScriptName` = 'mob_chromatic_elite_guard' WHERE `entry` = 10814; +UPDATE `creature_template` SET `ScriptName` = 'mob_ancient_core_hound' WHERE `entry` = 11673; +UPDATE `creature_template` SET `ScriptName` = 'mob_core_rager' WHERE `entry` = 11672; +UPDATE `creature_template` SET `ScriptName` = 'mob_firesworn' WHERE `entry` = 12099; +UPDATE `creature_template` SET `ScriptName` = 'mob_firewalker' WHERE `entry` = 11666; +UPDATE `creature_template` SET `ScriptName` = 'mob_flame_guard' WHERE `entry` = 11667; +UPDATE `creature_template` SET `ScriptName` = 'mob_flamewaker' WHERE `entry` = 11661; +UPDATE `creature_template` SET `ScriptName` = 'mob_flamewaker_elite' WHERE `entry` = 11664; +UPDATE `creature_template` SET `ScriptName` = 'mob_flamewaker_healer' WHERE `entry` = 11663; +UPDATE `creature_template` SET `ScriptName` = 'mob_flamewaker_protector' WHERE `entry` = 12119; +UPDATE `creature_template` SET `ScriptName` = 'mob_lavaspawn' WHERE `entry` = 12265; +UPDATE `creature_template` SET `ScriptName` = 'mob_molten_destroyer' WHERE `entry` = 11659; +UPDATE `creature_template` SET `ScriptName` = 'mob_molten_giant' WHERE `entry` = 11658; +UPDATE `creature_template` SET `ScriptName` = 'boss_azuregos' WHERE `entry` = 6109; + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r91.sql b/src/bindings/scripts/sql/Updates/r91.sql new file mode 100644 index 00000000000..bd7fc1131ac --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r91.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template` SET `ScriptName` = 'npc_great_bear_spirit' WHERE `entry` = 11956; +UPDATE `creature_template` SET `ScriptName` = 'npcs_rutgar_and_frankal.cpp' WHERE `entry` IN (15170, 15171); \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r92.sql b/src/bindings/scripts/sql/Updates/r92.sql new file mode 100644 index 00000000000..b149b69c19c --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r92.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npcs_rutgar_and_frankal' WHERE `entry` IN (15170, 15171); \ No newline at end of file diff --git a/src/bindings/scripts/sql/Updates/r97.sql b/src/bindings/scripts/sql/Updates/r97.sql new file mode 100644 index 00000000000..95934d6ce15 --- /dev/null +++ b/src/bindings/scripts/sql/Updates/r97.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `ScriptName` = 'npcs_captains_blackanvil_and_skullsplit' WHERE `entry` IN (15440, 15612); diff --git a/src/bindings/scripts/sql/create_database.sql b/src/bindings/scripts/sql/create_database.sql new file mode 100644 index 00000000000..747dab2acb1 --- /dev/null +++ b/src/bindings/scripts/sql/create_database.sql @@ -0,0 +1,4 @@ +CREATE DATABASE `scriptdev2` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; + +GRANT ALL PRIVILEGES ON `scriptdev2` . * TO 'mangos'@'localhost' WITH GRANT OPTION; + diff --git a/src/bindings/scripts/sql/mangos_full_scripts.sql b/src/bindings/scripts/sql/mangos_full_scripts.sql new file mode 100644 index 00000000000..d63662a5866 --- /dev/null +++ b/src/bindings/scripts/sql/mangos_full_scripts.sql @@ -0,0 +1,871 @@ +/* */ + +/* WORLD BOSS */ +UPDATE `creature_template` SET `ScriptName`='boss_ysondre' WHERE `entry`=14887; +UPDATE `creature_template` SET `ScriptName`='boss_emeriss' WHERE `entry`=14889; +UPDATE `creature_template` SET `ScriptName`='boss_taerar' WHERE `entry`=14890; +UPDATE `creature_template` SET `ScriptName`='boss_shade_of_taerar' WHERE `entry`=15302; +UPDATE `creature_template` SET `ScriptName`='boss_kruul' WHERE `entry`=18338; +UPDATE `creature_template` SET `ScriptName`='boss_azuregos' WHERE `entry`=6109; +UPDATE `creature_template` SET `ScriptName`='mob_dementeddruids' WHERE `entry`=15260; + +/* GO */ +UPDATE `gameobject_template` SET `ScriptName`='go_northern_crystal_pylon' WHERE `entry`=164955; +UPDATE `gameobject_template` SET `ScriptName`='go_western_crystal_pylon' WHERE `entry`=164956; +UPDATE `gameobject_template` SET `ScriptName`='go_eastern_crystal_pylon' WHERE `entry`=164957; +UPDATE `gameobject_template` SET `ScriptName`='go_barov_journal' WHERE `entry`=180794; +UPDATE `gameobject_template` SET `ScriptName`='go_field_repair_bot_74A' where `entry`= 179552; +UPDATE `gameobject_template` SET `ScriptName`='go_orb_of_command' WHERE `entry`=179879; +UPDATE `gameobject_template` SET `ScriptName`='go_tablet_of_madness' WHERE `entry`=180368; +UPDATE `gameobject_template` SET `ScriptName`='go_tablet_of_the_seven' WHERE `entry`=169294; + +/* GUARD */ +UPDATE `creature_template` SET `ScriptName`='guard_azuremyst' WHERE `entry`=18038; +UPDATE `creature_template` SET `ScriptName`='guard_orgrimmar' WHERE `entry`=3296; +UPDATE `creature_template` SET `ScriptName`='guard_stormwind' WHERE `entry` IN (68,1976); +UPDATE `creature_template` SET `ScriptName`='guard_contested' WHERE `entry` IN (9460,4624,3502,11190,15184); +UPDATE `creature_template` SET `ScriptName`='guard_elwynnforest' WHERE `entry`=1423; +UPDATE `creature_template` SET `ScriptName`='guard_eversong' WHERE `entry`=16221; +UPDATE `creature_template` SET `ScriptName`='guard_darnassus' WHERE `entry`=4262; +UPDATE `creature_template` SET `ScriptName`='guard_teldrassil' WHERE `entry`=3571; +UPDATE `creature_template` SET `ScriptName`='guard_ironforge' WHERE `entry`=5595; +UPDATE `creature_template` SET `ScriptName`='guard_dunmorogh' WHERE `entry` IN (727,13076); +UPDATE `creature_template` SET `ScriptName`='guard_undercity' WHERE `entry`=5624; +UPDATE `creature_template` SET `ScriptName`='guard_bluffwatcher' WHERE `entry`=3084; +UPDATE `creature_template` SET `ScriptName`='guard_durotar' WHERE `entry`=5953; +UPDATE `creature_template` SET `ScriptName`='guard_mulgore' WHERE `entry` IN (3212,3215,3217,3218,3219,3220,3221,3222,3223,3224); +UPDATE `creature_template` SET `ScriptName`='guard_dunmorogh' WHERE `entry` IN (727,13076); +UPDATE `creature_template` SET `ScriptName`='guard_tirisfal' WHERE `entry` IN (1735,1738,2210,1744,1745,5725,1743,2209,1746,1742); +UPDATE `creature_template` SET `ScriptName`='guard_silvermoon' WHERE `entry`=16222; +UPDATE `creature_template` SET `ScriptName`='guard_exodar' WHERE `entry`=16733; +UPDATE `creature_template` SET `ScriptName`='guard_shattrath' WHERE `entry`=19687; +UPDATE `creature_template` SET `ScriptName`='guard_shattrath_aldor' WHERE `entry`=18549; +UPDATE `creature_template` SET `ScriptName`='guard_shattrath_scryer' WHERE `entry`=18568; + +/* ITEM */ +UPDATE `item_template` SET `ScriptName`='item_area_52_special' WHERE `entry`=28132; +UPDATE `item_template` SET `ScriptName`='item_attuned_crystal_cores' WHERE `entry`=34368; +UPDATE `item_template` SET `ScriptName`='item_blackwhelp_net' WHERE `entry`=31129; +UPDATE `item_template` SET `ScriptName`='item_disciplinary_rod' WHERE `entry`=22473; +UPDATE `item_template` SET `ScriptName`='item_draenei_fishing_net' WHERE `entry`=23654; +UPDATE `item_template` SET `ScriptName`='item_flying_machine' WHERE `entry` IN (34060,34061); +UPDATE `item_template` SET `ScriptName`='item_gor_dreks_ointment' WHERE `entry`=30175; +UPDATE `item_template` SET `ScriptName`='item_muiseks_vessel' WHERE `entry` IN (9606,9618,9619,9620,9621); +UPDATE `item_template` SET `ScriptName`='item_nether_wraith_beacon' WHERE `entry`=31742; +UPDATE `item_template` SET `ScriptName`='item_protovoltaic_magneto_collector' WHERE `entry`=30656; +UPDATE `item_template` SET `ScriptName`='item_razorthorn_flayer_gland' WHERE `entry`=34255; +UPDATE `item_template` SET `ScriptName`='item_soul_cannon' WHERE `entry`=32825; +UPDATE `item_template` SET `ScriptName`='item_sparrowhawk_net' WHERE `entry`=32321; +UPDATE `item_template` SET `ScriptName`='item_tainted_core' WHERE `entry`=31088; +UPDATE `item_template` SET `ScriptName`='item_tame_beast_rods' WHERE `entry` IN (15908,15911,15913,15914,15915,15916,15917,15919,15920,15921,15922,15923,23697,23702,23703,23896,23897,23898); +UPDATE `item_template` SET `ScriptName`='item_voodoo_charm' WHERE `entry`=8149; +UPDATE `item_template` SET `ScriptName`='item_vorenthals_presence' WHERE `entry`=30259; +UPDATE `item_template` SET `ScriptName`='item_yehkinyas_bramble' WHERE `entry`=10699; +UPDATE `item_template` SET `ScriptName`='item_zezzaks_shard' WHERE `entry`=31463; + +/* NPC (usually creatures to be found in more than one specific zone) */ +UPDATE `creature_template` SET `ScriptName`='npc_chicken_cluck' WHERE `entry`=620; +UPDATE `creature_template` SET `ScriptName`='npc_dancing_flames' WHERE `entry`=25305; +UPDATE `creature_template` SET `ScriptName`='npc_guardian' WHERE `entry`=5764; +UPDATE `creature_template` SET `ScriptName`='npc_mount_vendor' WHERE `entry` IN (384,1261,1460,2357,3362,3685,4730,4731,4885,7952,7955,16264,17584); +UPDATE `creature_template` SET `ScriptName`='npc_doctor' WHERE `entry` IN (12939,12920); +UPDATE `creature_template` SET `ScriptName`='npc_injured_patient' WHERE `entry` IN (12936,12937,12938,12923,12924,12925); +UPDATE `creature_template` SET `ScriptName`='npc_prof_alchemy' WHERE `entry` IN (17909,19052,22427); +UPDATE `creature_template` SET `ScriptName`='npc_prof_blacksmith' WHERE `entry` IN (5164,11145,11146,11176,11177,11178,11191,11192,11193); +UPDATE `creature_template` SET `ScriptName`='npc_prof_leather' WHERE `entry` IN (7866,7867,7868,7869,7870,7871); +UPDATE `creature_template` SET `ScriptName`='npc_prof_tailor' WHERE `entry` IN (22208,22212,22213); +UPDATE `creature_template` SET `ScriptName`='npc_rogue_trainer' WHERE `entry` IN (918,4163,3328,4583,5165,5167,13283,16684); +UPDATE `creature_template` SET `ScriptName`='npc_sayge' WHERE `entry`=14822; + + +/* */ +/* ZONE */ +/* */ + +/* ALTERAC MOUNTAINS */ +UPDATE `creature_template` SET `ScriptName`='npc_ravenholdt' WHERE `entry`=13936; + +/* ALTERAC VALLEY */ + + +/* ARATHI HIGHLANDS */ + + +/* ASHENVALE */ + + +/* */ +/* AUCHINDOUN */ +/* */ + +/* MANA TOMBS */ +UPDATE `creature_template` SET `ScriptName`='boss_pandemonius' WHERE `entry`=18341; +UPDATE `creature_template` SET `ScriptName`='boss_nexusprince_shaffar' WHERE `entry`=18344; +UPDATE `creature_template` SET `ScriptName`='mob_ethereal_beacon' WHERE `entry`=18431; + +/* AUCHENAI CRYPTS */ +UPDATE `creature_template` SET `ScriptName`='boss_exarch_maladaar' WHERE `entry`=18373; +UPDATE `creature_template` SET `ScriptName`='mob_avatar_of_martyred' WHERE `entry`=18478; +UPDATE `creature_template` SET `ScriptName`='mob_stolen_soul' WHERE `entry`=18441; + +/* SETHEKK HALLS */ +UPDATE `instance_template` SET `script`='instance_sethekk_halls' WHERE `map`=556; +UPDATE `creature_template` SET `ScriptName`='mob_syth_fire' WHERE `entry`=19203; +UPDATE `creature_template` SET `ScriptName`='mob_syth_arcane' WHERE `entry`=19205; +UPDATE `creature_template` SET `ScriptName`='mob_syth_frost' WHERE `entry`=19204; +UPDATE `creature_template` SET `ScriptName`='mob_syth_shadow' WHERE `entry`=19206; +UPDATE `creature_template` SET `ScriptName`='boss_talon_king_ikiss' WHERE `entry`=18473; +UPDATE `creature_template` SET `ScriptName`='boss_darkweaver_syth' WHERE `entry`=18472; + +/* SHADOW LABYRINTH */ +UPDATE `instance_template` SET `script`='instance_shadow_labyrinth' WHERE `map`=555; +UPDATE `creature_template` SET `ScriptName`='boss_murmur' WHERE `entry`=18708; +UPDATE `creature_template` SET `ScriptName`='boss_grandmaster_vorpil' WHERE `entry`=18732; +UPDATE `creature_template` SET `ScriptName`='boss_blackheart_the_inciter' WHERE `entry`=18667; +UPDATE `creature_template` SET `ScriptName`='boss_ambassador_hellmaw' WHERE `entry`=18731; + +/* AZSHARA */ +UPDATE `creature_template` SET `ScriptName`='mobs_spitelashes' WHERE `entry` IN (6190,6193,6194,6195,6196,7885,7886,12204,12205); +UPDATE `creature_template` SET `ScriptName`='npc_loramus_thalipedes' WHERE `entry`=7783; + +/* AZUREMYST ISLE */ +UPDATE `creature_template` SET `ScriptName`='npc_engineer_spark_overgrind' WHERE `entry`=17243; +UPDATE `creature_template` SET `ScriptName`='npc_injured_draenei' WHERE `entry`=16971; +UPDATE `creature_template` SET `ScriptName`='npc_susurrus' WHERE `entry`=17435; + +/* BADLANDS */ + + +/* BARRENS */ +UPDATE `creature_template` SET `ScriptName`='npc_beaten_corpse' WHERE `entry`=10668; +UPDATE `creature_template` SET `ScriptName`='npc_sputtervalve' WHERE `entry`=3442; +UPDATE `creature_template` SET `ScriptName`='npc_taskmaster_fizzule' WHERE `entry`=7233; + +/* BLACK TEMPLE */ +UPDATE `instance_template` SET `script`='instance_black_temple' WHERE `map`=564; +UPDATE `creature_template` SET `ScriptName`='npc_akama_shade' WHERE `entry`=22990; -- Akama at Shade of Akama +UPDATE `creature_template` SET `ScriptName`='npc_akama_illidan' WHERE `entry`=23089; -- Akama at Illidan +UPDATE `creature_template` SET `ScriptName`='mob_illidari_council' WHERE `entry`=23426; -- Illidari Council controller mob +UPDATE `creature_template` SET `ScriptName`='mob_blood_elf_council_voice_trigger' WHERE `entry` = 23499; -- Voice Trigger Mob (Controls Aggro + Enrage yells) +UPDATE `creature_template` SET `ScriptName`='boss_veras_darkshadow' WHERE `entry`=22952; -- Rogue of Illidari Council +UPDATE `creature_template` SET `ScriptName`='boss_teron_gorefiend' WHERE `entry`=22871; -- Teron Gorefiend +UPDATE `creature_template` SET `ScriptName`='boss_supremus' WHERE `entry`=22898; -- Supremus +UPDATE `creature_template` SET `ScriptName`='boss_shade_of_akama' WHERE `entry`=22841; -- Shade of Akama +UPDATE `creature_template` SET `ScriptName`='boss_reliquary_of_souls' WHERE `entry`=22856; -- Reliquary Controller Mob +UPDATE `creature_template` SET `ScriptName`='boss_essence_of_suffering' WHERE `entry`=23418; -- Essence Of Suffering +UPDATE `creature_template` SET `ScriptName`='boss_essence_of_desire' WHERE `entry`=23419; -- Essence of Desire +UPDATE `creature_template` SET `ScriptName`='boss_essence_of_anger' WHERE `entry`=23420; -- Essence of Anger +UPDATE `creature_template` SET `ScriptName`='boss_najentus' WHERE `entry`=22887; -- High Warlord Naj'entus +UPDATE `creature_template` SET `ScriptName`='boss_gurtogg_bloodboil' WHERE `entry`=22948; -- Gurtogg Bloodboil +UPDATE `creature_template` SET `ScriptName`='boss_mother_shahraz' WHERE `entry`=22947; -- Mother Shahraz +UPDATE `creature_template` SET `ScriptName`='boss_lady_malande' WHERE `entry`=22951; -- Priest <3 at Illidari Council +UPDATE `creature_template` SET `ScriptName`='boss_illidan_stormrage' WHERE `entry`=22917; -- Illidan The Betrayer! +UPDATE `creature_template` SET `ScriptName`='boss_high_nethermancer_zerevor' WHERE `entry`=22950; -- Mage at Illidari Council +UPDATE `creature_template` SET `ScriptName`='boss_gathios_the_shatterer' WHERE `entry`=22949; -- Paladin at Illidari Council +UPDATE `creature_template` SET `ScriptName`='boss_maiev_shadowsong' WHERE `entry`=23197; -- Maiev Shadowsong +UPDATE `gameobject_template` SET `ScriptName`='gameobject_cage_trap' WHERE `entry`=185916; -- Cage Trap GO in Illidan Encounter +UPDATE `creature_template` SET `ScriptName`='mob_blaze' WHERE `entry`=23259; -- Blaze mob in Illidan Phase 2 +UPDATE `creature_template` SET `ScriptName`='mob_flame_of_azzinoth' WHERE `entry`=22997; -- Flame of Azzinoth (Illidan Phase 2) +UPDATE `creature_template` SET `ScriptName`='mob_blade_of_azzinoth' WHERE `entry`=22996; -- Blade of Azzinoth (Illidan Phase 2) +UPDATE `creature_template` SET `ScriptName`='mob_demon_fire' WHERE `entry`=23069; -- Demon Fire in Illidan Phase 2 +UPDATE `creature_template` SET `ScriptName`='mob_flame_crash' WHERE `entry`=23336; -- Flame Crash in Illidan Normal Form +UPDATE `creature_template` SET `ScriptName`='mob_cage_trap_trigger' WHERE `entry`=23304; -- Cage Trap mob in Illidan Phase 3/4 Normal +UPDATE `creature_template` SET `ScriptName`='mob_shadow_demon' WHERE `entry`=23375; -- Shadow Demon in Illidan Demon Form +UPDATE `creature_template` SET `ScriptName`='npc_volcano' WHERE `entry`=23085; -- Supremus Volcano +UPDATE `creature_template` SET `ScriptName`='molten_flame' WHERE `entry`=23095; -- Molten Flame in SUpremus +UPDATE `creature_template` SET `ScriptName`='mob_ashtongue_channeler' WHERE `entry`=23421; -- Ashtongue CHanneler in Shade of AKama +UPDATE `creature_template` SET `ScriptName`='mob_ashtongue_sorcerer' WHERE `entry`=23215; -- Ashtongue Sorcerer in Shade of Akama +UPDATE `creature_template` SET `ScriptName`='npc_enslaved_soul' WHERE `entry`=23469; -- Enslaved Soul in Reliquary Event +UPDATE `creature_template` SET `ScriptName`='mob_doom_blossom' WHERE `entry`=23123; -- Doom Blossoms in Teron Gorefiend's encounter +UPDATE `creature_template` SET `ScriptName`='npc_spirit_of_olum' WHERE `entry`=23411; +-- UPDATE `creature_template` SET `ScriptName`='mob_shadowy_construct' WHERE `entry`=23111; -- Shadowy Construct in Teron Gorefiend's encounter. Commented until Mind Control is implemented. + +/* BLACKFATHOM DEPTHS */ + + +/* BLACKROCK DEPTHS */ +UPDATE `creature_template` SET `ScriptName`='boss_emperor_dagran_thaurissan' WHERE `entry`=9019; +UPDATE `creature_template` SET `ScriptName`='boss_moira_bronzebeard' WHERE `entry`=8929; +UPDATE `creature_template` SET `ScriptName`='boss_ambassador_flamelash' WHERE `entry`=9156; +UPDATE `creature_template` SET `ScriptName`='boss_angerrel' WHERE `entry`=9035; +UPDATE `creature_template` SET `ScriptName`='boss_anubshiah' WHERE `entry`=9031; +UPDATE `creature_template` SET `ScriptName`='boss_doomrel' WHERE `entry`=9039; +UPDATE `creature_template` SET `ScriptName`='boss_doperel' WHERE `entry`=9040; +UPDATE `creature_template` SET `ScriptName`='boss_general_angerforge' WHERE `entry`=9033; +UPDATE `creature_template` SET `ScriptName`='boss_gloomrel' WHERE `entry`=9037; +UPDATE `creature_template` SET `ScriptName`='boss_gorosh_the_dervish' WHERE `entry`=9027; +UPDATE `creature_template` SET `ScriptName`='boss_grizzle' WHERE `entry`=9028; +UPDATE `creature_template` SET `ScriptName`='boss_haterel' WHERE `entry`=9034; +UPDATE `creature_template` SET `ScriptName`='boss_high_interrogator_gerstahn' WHERE `entry`=9018; +UPDATE `creature_template` SET `ScriptName`='boss_magmus' WHERE `entry`=9938; +UPDATE `creature_template` SET `ScriptName`='boss_seethrel' WHERE `entry`=9038; +UPDATE `creature_template` SET `ScriptName`='boss_vilerel' WHERE `entry`=9036; +UPDATE `creature_template` SET `ScriptName`='phalanx' WHERE `entry`=9502; +UPDATE `creature_template` SET `ScriptName`='npc_lokhtos_darkbargainer' WHERE `entry`=12944; +UPDATE `creature_template` SET `ScriptName`='npc_kharan_mighthammer' WHERE `entry`=9021; + +/* BLACKROCK SPIRE */ +/* BLACKROCK SPIRE Lower bosses */ +UPDATE `creature_template` SET `ScriptName`='boss_highlord_omokk' WHERE `entry`=9196; +UPDATE `creature_template` SET `ScriptName`='boss_shadow_hunter_voshgajin' WHERE `entry`=9236; +UPDATE `creature_template` SET `ScriptName`='boss_warmaster_voone' WHERE `entry`=9237; +UPDATE `creature_template` SET `ScriptName`='boss_mother_smolderweb' WHERE `entry`=10596; +UPDATE `creature_template` SET `ScriptName`='quartermaster_zigris' WHERE `entry`=9736; +UPDATE `creature_template` SET `ScriptName`='boss_halycon' WHERE `entry`=10220; +UPDATE `creature_template` SET `ScriptName`='boss_overlord_wyrmthalak' WHERE `entry`=9568; +/* BLACKROCK SPIRE Upper bosses */ +UPDATE `creature_template` SET `ScriptName`='boss_the_beast' WHERE `entry`=10430; +UPDATE `creature_template` SET `ScriptName`='boss_drakkisath' WHERE `entry`=10363; +UPDATE `creature_template` SET `ScriptName`='boss_gyth' WHERE `entry`=10339; +UPDATE `creature_template` SET `ScriptName`='boss_rend_blackhand' WHERE `entry`=10429; +UPDATE `creature_template` SET `ScriptName`='boss_pyroguard_emberseer' WHERE `entry`=9816; + +/* BLACKWING LAIR */ +UPDATE `instance_template` SET `script`='instance_blackwing_lair' WHERE `map`=469; +UPDATE `creature_template` SET `ScriptName`='boss_razorgore' WHERE `entry`=12435; +UPDATE `creature_template` SET `ScriptName`='boss_vaelastrasz' WHERE `entry`=13020; +UPDATE `creature_template` SET `ScriptName`='boss_broodlord' WHERE `entry`=12017; +UPDATE `creature_template` SET `ScriptName`='boss_firemaw' WHERE `entry`=11983; +UPDATE `creature_template` SET `ScriptName`='boss_ebonroc' WHERE `entry`=14601; +UPDATE `creature_template` SET `ScriptName`='boss_flamegor' WHERE `entry`=11981; +UPDATE `creature_template` SET `ScriptName`='boss_chromaggus' WHERE `entry`=14020; +UPDATE `creature_template` SET `ScriptName`='boss_victor_nefarius' WHERE `entry`=10162; +UPDATE `creature_template` SET `ScriptName`='boss_nefarian' WHERE `entry`=11583; + +/* BLADE'S EDGE MOUNTAINS */ +UPDATE `creature_template` SET `ScriptName`='mobs_bladespire_ogre' WHERE `entry` IN (19998,20334,21296,21975); +UPDATE `creature_template` SET `ScriptName`='mobs_nether_drake' WHERE `entry` IN (20021,21817,21820,21821,21823); +UPDATE `creature_template` SET `ScriptName`='npc_daranelle' WHERE `entry`=21469; +UPDATE `creature_template` SET `ScriptName`='npc_overseer_nuaar' WHERE `entry`=21981; +UPDATE `creature_template` SET `ScriptName`='npc_saikkal_the_elder' WHERE `entry`=22932; +UPDATE `creature_template` SET `ScriptName`='npc_skyguard_handler_deesak' WHERE `entry`=23415; + +/* BLASTED LANDS */ +UPDATE `creature_template` SET `ScriptName`='npc_deathly_usher' WHERE `entry`=8816; +UPDATE `creature_template` SET `ScriptName`='npc_fallen_hero_of_horde' WHERE `entry`=7572; + +/* BLOODMYST ISLE */ +UPDATE `creature_template` SET `ScriptName`='mob_webbed_creature' WHERE `entry`=17680; +UPDATE `creature_template` SET `ScriptName`='npc_captured_sunhawk_agent' WHERE `entry`=17824; + +/* BURNING STEPPES */ +UPDATE `creature_template` SET `ScriptName`='npc_ragged_john' WHERE `entry`=9563; + +/* */ +/* CAVERNS OF TIME */ +/* */ + +/* MT. HYJAL */ +UPDATE `instance_template` SET `script`='instance_hyjal' WHERE `map`=534; +UPDATE `creature_template` SET `ScriptName`='npc_tyrande_whisperwind' WHERE `entry`=17948; +UPDATE `creature_template` SET `ScriptName`='npc_thrall' WHERE `entry` =17852; +UPDATE `creature_template` SET `ScriptName`='npc_jaina_proudmoore' WHERE `entry`=17772; +UPDATE `creature_template` SET `ScriptName`='boss_archimonde' WHERE `entry`=17968; +UPDATE `creature_template` SET `ScriptName`='mob_doomfire' WHERE `entry`=18095; +UPDATE `creature_template` SET `ScriptName`='mob_doomfire_targetting' WHERE `entry`=18104; +UPDATE `creature_template` SET `ScriptName`='mob_ancient_wisp' WHERE `entry`=17946; + +/* OLD HILLSBRAD */ +UPDATE `instance_template` SET `script`='instance_old_hillsbrad' WHERE `map`=560; +UPDATE `creature_template` SET `ScriptName`='boss_lieutenant_drake' WHERE `entry`=17848; +UPDATE `creature_template` SET `ScriptName`='boss_epoch_hunter' WHERE `entry`=18096; +UPDATE `creature_template` SET `ScriptName`='boss_captain_skarloc' WHERE `entry`=17862; +UPDATE `creature_template` SET `ScriptName`='npc_brazen' WHERE `entry`=18725; +UPDATE `creature_template` SET `ScriptName`='npc_erozion' WHERE `entry`=18723; +UPDATE `creature_template` SET `ScriptName`='npc_taretha' WHERE `entry`=18887; +UPDATE `creature_template` SET `ScriptName`='npc_thrall_old_hillsbrad' WHERE `entry`=17876; + +/* THE DARK PORTAL */ +UPDATE `creature_template` SET `ScriptName`='boss_chrono_lord_deja' WHERE `entry`=17879; +UPDATE `creature_template` SET `ScriptName`='boss_aeonus' WHERE `entry`=17881; +UPDATE `creature_template` SET `ScriptName`='boss_temporus' WHERE `entry`=17880; + + +/* */ +/* COILFANG RESERVOIR */ +/* */ + +/* THE SLAVE PENS */ + +/* THE UNDERBOG */ +UPDATE `creature_template` SET `ScriptName`='mob_underbog_mushroom' WHERE `entry`=17990; +UPDATE `creature_template` SET `ScriptName`='boss_hungarfen' WHERE `entry`=17770; + +/* THE STEAMVAULT */ +UPDATE `instance_template` SET `script`='instance_steam_vault' WHERE `map`=545; +UPDATE `creature_template` SET `ScriptName`='boss_hydromancer_thespia' WHERE `entry`=17797; +UPDATE `creature_template` SET `ScriptName`='boss_mekgineer_steamrigger' WHERE `entry`=17796; +UPDATE `creature_template` SET `ScriptName`='boss_warlord_kalithresh' WHERE `entry`=17798; +UPDATE `creature_template` SET `ScriptName`='mob_coilfang_waterelemental' WHERE `entry`=17917; +UPDATE `creature_template` SET `ScriptName`='mob_naga_distiller' WHERE `entry`=17954; +UPDATE `creature_template` SET `ScriptName`='mob_steamrigger_mechanic' WHERE `entry`=17951; + +/* SERPENTSHRINE CAVERN */ +UPDATE `instance_template` SET `script`='instance_serpent_shrine' WHERE `map`=548; +UPDATE `creature_template` SET `ScriptName`='boss_hydross_the_unstable' WHERE `entry`=21216; +/* Leotheras the Blind event */ +UPDATE `creature_template` SET `ScriptName`='boss_leotheras_the_blind' WHERE `entry`=21215; +UPDATE `creature_template` SET `ScriptName`='boss_leotheras_the_blind_demonform' WHERE `entry`=21845; +/* Fathom-lord Karathress event */ +UPDATE `creature_template` SET `ScriptName`='boss_fathomlord_karathress' WHERE `entry`=21214; +UPDATE `creature_template` SET `ScriptName`='boss_fathomguard_sharkkis' WHERE `entry`=21966; +UPDATE `creature_template` SET `ScriptName`='boss_fathomguard_tidalvess' WHERE `entry`=21965; +UPDATE `creature_template` SET `ScriptName`='boss_fathomguard_caribdis' WHERE `entry`=21964; +/* Morogrim Tidewalker event */ +UPDATE `creature_template` SET `ScriptName`='boss_morogrim_tidewalker' WHERE `entry`=21213; +UPDATE `creature_template` SET `ScriptName`='mob_water_globule' WHERE `entry`=21913; +/* Lady Vashj event */ +UPDATE `creature_template` SET `ScriptName`='boss_lady_vashj' WHERE `entry`=21212; +UPDATE `creature_template` SET `ScriptName`='mob_enchanted_elemental' WHERE `entry`=21958; +UPDATE `creature_template` SET `ScriptName`='mob_tainted_elemental' WHERE `entry`=22009; +UPDATE `creature_template` SET `ScriptName`='mob_coilfang_elite' WHERE `entry`=22055; +UPDATE `creature_template` SET `ScriptName`='mob_coilfang_strider' WHERE `entry`=22056; +UPDATE `creature_template` SET `ScriptName`='mob_fathom_sporebat' WHERE `entry`=22140; +UPDATE `creature_template` SET `ScriptName`='mob_shield_generator_channel' WHERE `entry`=19870; + + +/* DARKSHORE */ + + +/* DEADMINES */ + + +/* DEADWIND PASS */ + + +/* DESOLACE */ + + +/* DIRE MAUL */ + + +/* DUN MOROGH */ +UPDATE `creature_template` SET `ScriptName`='npc_narm_faulk' WHERE `entry`=6177; + + +/* DUROTAR */ + + +/* DUSKWOOD */ + + +/* DUSTWALLOW MARSH */ +UPDATE `creature_template` SET `ScriptName`='mobs_risen_husk_spirit' WHERE `entry` IN (23554,23555); +UPDATE `creature_template` SET `ScriptName`='npc_deserter_agitator' WHERE `entry`=23602; +UPDATE `creature_template` SET `ScriptName`='npc_lady_jaina_proudmoore' WHERE `entry`=4968; +UPDATE `creature_template` SET `ScriptName`='npc_nat_pagle' WHERE `entry`=12919; +UPDATE `creature_template` SET `ScriptName`='npc_restless_apparition' WHERE `entry`=23861; + +/* EASTERN PLAGUELANDS */ +UPDATE `creature_template` SET `ScriptName`='mobs_ghoul_flayer' WHERE `entry` IN (8530,8531,8532); +UPDATE `creature_template` SET `ScriptName`='npc_augustus_the_touched' WHERE `entry`=12384; +UPDATE `creature_template` SET `ScriptName`='npc_darrowshire_spirit' WHERE `entry`=11064; +UPDATE `creature_template` SET `ScriptName`='npc_tirion_fordring' WHERE `entry`=1855; + +/* ELWYNN FOREST */ +UPDATE `creature_template` SET `ScriptName`='npc_henze_faulk' WHERE `entry`=6172; + +/* EVERSONG WOODS */ +UPDATE `creature_template` SET `ScriptName`='mobs_mana_tapped' WHERE `entry` IN (15273,15274,15294,15298,15367); +UPDATE `creature_template` SET `ScriptName`='npc_prospector_anvilward' WHERE `entry`=15420; + +/* FELWOOD */ +UPDATE `creature_template` SET `ScriptName`='npcs_riverbreeze_and_silversky' WHERE `entry` IN (9528,9529); + +/* FERALAS */ +UPDATE `creature_template` SET `ScriptName`='npc_gregan_brewspewer' WHERE `entry`=7775; +UPDATE `creature_template` SET `ScriptName`='npc_screecher_spirit' WHERE `entry`=8612; + +/* GHOSTLANDS */ +UPDATE `creature_template` SET `ScriptName`='npc_blood_knight_dawnstar' WHERE `entry`=17832; +UPDATE `creature_template` SET `ScriptName`='npc_budd_nedreck' WHERE `entry`=23559; +UPDATE `creature_template` SET `ScriptName`='npc_rathis_tomber' WHERE `entry`=16224; + +/* GNOMEREGAN */ + + +/* GRUUL'S LAIR */ +UPDATE `instance_template` SET `script`='instance_gruuls_lair' WHERE `map` =565; +UPDATE `creature_template` SET `ScriptName`='boss_gruul' WHERE `entry`=19044; +/* Maulgar and Event */ +UPDATE `creature_template` SET `ScriptName`='boss_high_king_maulgar' WHERE `entry`=18831; +UPDATE `creature_template` SET `ScriptName`='boss_kiggler_the_crazed' WHERE `entry`=18835; +UPDATE `creature_template` SET `ScriptName`='boss_blindeye_the_seer' WHERE `entry`=18836; +UPDATE `creature_template` SET `ScriptName`='boss_olm_the_summoner' WHERE `entry`=18834; +UPDATE `creature_template` SET `ScriptName`='boss_krosh_firehand' WHERE `entry`=18832; + +/* */ +/* HELLFIRE CITADEL */ +/* */ + +/* BLOOD FURNACE */ +/* The Maker,Broggok,Kelidan,Broggok's cloud */ +UPDATE `creature_template` SET `ScriptName`='boss_the_maker' WHERE `entry`=17381; +UPDATE `creature_template` SET `ScriptName`='boss_broggok' WHERE `entry`=17380; +UPDATE `creature_template` SET `ScriptName`='boss_kelidan_the_breaker' WHERE `entry`=17377; +UPDATE `creature_template` SET `ScriptName`='mob_broggok_poisoncloud' WHERE `entry`=17662; +UPDATE `creature_template` SET `ScriptName`='mob_shadowmoon_channeler' WHERE `entry`=17653; + +/* HELLFIRE RAMPARTS */ +/* Vazruden,Omor the Unscarred,Watchkeeper Gargolmar */ +UPDATE `creature_template` SET `ScriptName`='boss_omor_the_unscarred' WHERE `entry`=17308; +UPDATE `creature_template` SET `ScriptName`='boss_watchkeeper_gargolmar' WHERE `entry`=17306; + +/* SHATTERED HALLS */ +/* Nethekurse and his spawned shadowfissure */ +UPDATE `creature_template` SET `ScriptName`='boss_grand_warlock_nethekurse' WHERE `entry`=16807; +UPDATE `creature_template` SET `ScriptName`='boss_warbringer_omrogg' WHERE `entry`=16809; +UPDATE `creature_template` SET `ScriptName`='mob_fel_orc_convert' WHERE `entry`=17083; +UPDATE `creature_template` SET `ScriptName`='mob_lesser_shadow_fissure' WHERE `entry`=17471; +UPDATE `creature_template` SET `ScriptName`='mob_omrogg_heads' WHERE `entry` IN (19523,19524); +UPDATE `instance_template` SET `script`='instance_shattered_halls' WHERE `map`=540; + +/* MAGTHERIDON'S LAIR */ +UPDATE `instance_template` SET `script`='instance_magtheridons_lair' WHERE `map`=544; +UPDATE `gameobject_template` SET `ScriptName`='go_manticron_cube' WHERE `entry`=181713; +UPDATE `creature_template` SET `ScriptName`='boss_magtheridon' WHERE `entry` =17257; +UPDATE `creature_template` SET `ScriptName`='mob_hellfire_channeler' WHERE `entry`=17256; + +/* HELLFIRE PENINSULA */ +UPDATE `creature_template` SET `ScriptName`='boss_doomlord_kazzak' WHERE `entry`=18728; +UPDATE `creature_template` SET `ScriptName`='npc_wing_commander_brack' WHERE `entry`=19401; +UPDATE `creature_template` SET `ScriptName`='npc_wing_commander_dabiree' WHERE `entry`=19409; +UPDATE `creature_template` SET `ScriptName`='npc_gryphoneer_windbellow' WHERE `entry`=20235; + +/* HILLSBRAD FOOTHILLS */ + + +/* HINTERLANDS */ + + +/* IRONFORGE */ +UPDATE `creature_template` SET `ScriptName`='npc_royal_historian_archesonus' WHERE `entry`=8879; + +/* ISLE OF QUEL'DANAS */ +UPDATE `creature_template` SET `ScriptName`='npc_ayren_cloudbreaker' WHERE `entry`=25059; +UPDATE `creature_template` SET `ScriptName`='npc_converted_sentry' WHERE `entry`=24981; +UPDATE `creature_template` SET `ScriptName`='npc_unrestrained_dragonhawk' WHERE `entry`=25236; + +/* KARAZHAN */ +UPDATE `instance_template` SET `script`='instance_karazhan' WHERE `map`=532; +UPDATE `creature_template` SET `ScriptName`='boss_midnight' WHERE `entry`=16151; +UPDATE `creature_template` SET `ScriptName`='boss_attumen' WHERE `entry`=15550; +UPDATE `creature_template` SET `ScriptName`='boss_moroes' WHERE `entry`=15687; +UPDATE `creature_template` SET `ScriptName`='boss_maiden_of_virtue' WHERE `entry`=16457; +UPDATE `creature_template` SET `ScriptName`='boss_curator' WHERE `entry`=15691; +UPDATE `creature_template` SET `ScriptName`='boss_julianne' WHERE `entry`=17534; +UPDATE `creature_template` SET `ScriptName`='boss_romulo' WHERE `entry`=17533; +UPDATE `creature_template` SET `ScriptName`='boss_dorothee' WHERE `entry`=17535; +UPDATE `creature_template` SET `ScriptName`='boss_strawman' WHERE `entry`=17543; +UPDATE `creature_template` SET `ScriptName`='boss_tinhead' WHERE `entry`=17547; +UPDATE `creature_template` SET `ScriptName`='boss_tito' WHERE `entry`=17548; +UPDATE `creature_template` SET `ScriptName`='boss_roar' WHERE `entry`=17546; +UPDATE `creature_template` SET `ScriptName`='boss_crone' WHERE `entry`=18168; +UPDATE `creature_template` SET `ScriptName`='boss_terestian_illhoof' WHERE `entry`=15688; +UPDATE `creature_template` SET `ScriptName`='boss_shade_of_aran' WHERE `entry`=16524; +UPDATE `creature_template` SET `ScriptName`='boss_netherspite' WHERE `entry`=15689; +UPDATE `creature_template` SET `ScriptName`='boss_malchezaar' WHERE `entry`=15690; +UPDATE `creature_template` SET `ScriptName`='boss_nightbane' WHERE `entry`=17225; +UPDATE `creature_template` SET `ScriptName`='boss_baroness_dorothea_millstipe' WHERE `entry`=19875; +UPDATE `creature_template` SET `ScriptName`='boss_baron_rafe_dreuger' WHERE `entry`=19874; +UPDATE `creature_template` SET `ScriptName`='boss_lady_catriona_von_indi' WHERE `entry`=19872; +UPDATE `creature_template` SET `ScriptName`='boss_lady_keira_berrybuck' WHERE `entry`=17007; +UPDATE `creature_template` SET `ScriptName`='boss_lord_robin_daris' WHERE `entry`=19876; +UPDATE `creature_template` SET `ScriptName`='boss_lord_crispin_ference' WHERE `entry`=19873; +UPDATE `creature_template` SET `ScriptName`='boss_bigbadwolf' WHERE `entry`=17521; +UPDATE `creature_template` SET `ScriptName`='mob_shadow_of_aran' WHERE `entry`=18254; +UPDATE `creature_template` SET `ScriptName`='mob_aran_elemental' WHERE `entry`=17167; +UPDATE `creature_template` SET `ScriptName`='mob_aran_blizzard' WHERE `entry`=17161; +UPDATE `creature_template` SET `ScriptName`='mob_homunculus' WHERE `entry`=16539; +UPDATE `creature_template` SET `ScriptName`='mob_kilrek' WHERE `entry`=17229; +UPDATE `creature_template` SET `ScriptName`='mob_demon_chain' WHERE `entry`=17248; +UPDATE `creature_template` SET `ScriptName`='mob_cyclone' WHERE `entry`=18412; +UPDATE `creature_template` SET `ScriptName`='netherspite_infernal' WHERE `entry`=17646; +UPDATE `creature_template` SET `ScriptName`='npc_berthold' WHERE `entry`=16153; +UPDATE `creature_template` SET `ScriptName`='npc_barnes' WHERE `entry`=16812; +UPDATE `creature_template` SET `ScriptName`='npc_grandmother' WHERE `entry`=17603; + + +/* LOCH MODAN */ +UPDATE `creature_template` SET `ScriptName`='npc_mountaineer_pebblebitty' WHERE `entry`=3836; + +/* Magister's Terrace */ +UPDATE `instance_template` SET `script`='instance_magisters_terrace' WHERE `map`=585; +UPDATE `creature_template` SET `ScriptName`='boss_selin_fireheart' WHERE `entry`=24723; +UPDATE `creature_template` SET `ScriptName`='mob_fel_crystal' WHERE `entry`=24722; +UPDATE `creature_template` SET `ScriptName`='boss_vexallus' WHERE `entry`=24744; +UPDATE `creature_template` SET `ScriptName`='mob_pure_energy' WHERE `entry`=24745; +UPDATE `creature_template` SET `ScriptName`='boss_priestess_delrissa' WHERE `entry`=24560; +UPDATE `creature_template` SET `ScriptName`='boss_kagani_nightstrike' WHERE `entry`=24557; +UPDATE `creature_template` SET `ScriptName`='boss_ellris_duskhallow' WHERE `entry`=24558; +UPDATE `creature_template` SET `ScriptName`='boss_eramas_brightblaze' WHERE `entry`=24554; +UPDATE `creature_template` SET `ScriptName`='boss_yazzai' WHERE `entry`=24561; +UPDATE `creature_template` SET `ScriptName`='boss_warlord_salaris' WHERE `entry`=24559; +UPDATE `creature_template` SET `ScriptName`='boss_garaxxas' WHERE `entry`=24555; +UPDATE `creature_template` SET `ScriptName`='mob_sliver' WHERE `entry`=24552; +UPDATE `creature_template` SET `ScriptName`='boss_apoko' WHERE `entry`=24553; +UPDATE `creature_template` SET `ScriptName`='boss_zelfan' WHERE `entry`=24556; +UPDATE `creature_template` SET `ScriptName`='boss_felblood_kaelthas' WHERE `entry`=24664; +UPDATE `creature_template` SET `ScriptName`='mob_arcane_sphere' WHERE `entry`=24708; +UPDATE `creature_template` SET `ScriptName`='mob_felkael_phoenix' WHERE `entry`=24674; +UPDATE `creature_template` SET `ScriptName`='mob_felkael_phoenix_egg' WHERE `entry`=24675; +UPDATE `creature_template` SET `ScriptName`='mob_felkael_flamestrike' WHERE `entry`=24666; + +/* MARAUDON */ +UPDATE `creature_template` SET `ScriptName`='boss_princess_theradras' WHERE `entry`=12201; +UPDATE `creature_template` SET `ScriptName`='boss_noxxion' WHERE `entry`=13282; +UPDATE `creature_template` SET `ScriptName`='boss_landslide' WHERE `entry`=12203; +UPDATE `creature_template` SET `ScriptName`='celebras_the_cursed' WHERE `entry`=12225; + +/* MOLTEN CORE */ +UPDATE `instance_template` SET `script`='instance_molten_core' WHERE `map`=409; +UPDATE `creature_template` SET `ScriptName`='boss_lucifron' WHERE `entry`=12118; +UPDATE `creature_template` SET `ScriptName`='boss_magmadar' WHERE `entry`=11982; +UPDATE `creature_template` SET `ScriptName`='boss_gehennas' WHERE `entry`=12259; +UPDATE `creature_template` SET `ScriptName`='boss_garr' WHERE `entry`=12057; +UPDATE `creature_template` SET `ScriptName`='boss_baron_geddon' WHERE `entry`=12056; +UPDATE `creature_template` SET `ScriptName`='boss_shazzrah' WHERE `entry`=12264; +UPDATE `creature_template` SET `ScriptName`='boss_golemagg' WHERE `entry`=11988; +UPDATE `creature_template` SET `ScriptName`='boss_sulfuron' WHERE `entry`=12098; +UPDATE `creature_template` SET `ScriptName`='boss_majordomo' WHERE `entry`=12018; +UPDATE `creature_template` SET `ScriptName`='boss_ragnaros' WHERE `entry`=11502; +UPDATE `creature_template` SET `ScriptName`='mob_ancient_core_hound' WHERE `entry`=11673; +UPDATE `creature_template` SET `ScriptName`='mob_firesworn' WHERE `entry`=12099; +UPDATE `creature_template` SET `ScriptName`='mob_core_rager' WHERE `entry`=11672; +UPDATE `creature_template` SET `ScriptName`='mob_flamewaker_priest' WHERE `entry`=11662; + +/* MOONGLADE */ +UPDATE `creature_template` SET `ScriptName`='npc_bunthen_plainswind' WHERE `entry`=11798; +UPDATE `creature_template` SET `ScriptName`='npc_great_bear_spirit' WHERE `entry`=11956; +UPDATE `creature_template` SET `ScriptName`='npc_silva_filnaveth' WHERE `entry`=11800; + +/* MULGORE */ +UPDATE `creature_template` SET `ScriptName`='npc_skorn_whitecloud' WHERE `entry`=3052; + +/* NAGRAND */ +UPDATE `creature_template` SET `ScriptName`='mob_lump' WHERE `entry`=18351; +UPDATE `creature_template` SET `ScriptName`='mob_shattered_rumbler' WHERE `entry`=17157; +UPDATE `creature_template` SET `ScriptName`='mob_sunspring_villager' WHERE `entry`=18240; +UPDATE `creature_template` SET `ScriptName`='npc_altruis_the_sufferer' WHERE `entry`=18417; +UPDATE `creature_template` SET `ScriptName`='npc_greatmother_geyah' WHERE `entry`=18141; +UPDATE `creature_template` SET `ScriptName`='npc_lantresor_of_the_blade' WHERE `entry`=18261; +UPDATE `creature_template` SET `ScriptName`='npc_creditmarker_visit_with_ancestors' WHERE `entry` IN (18840,18841,18842,18843); + +/* NAXXRAMAS */ +UPDATE `instance_template` SET `script`='instance_naxxramas' WHERE `map`=533; +UPDATE `creature_template` SET `ScriptName`='boss_anubrekhan' WHERE `entry`=15956; +UPDATE `creature_template` SET `ScriptName`='boss_faerlina' WHERE `entry`=15953; +UPDATE `creature_template` SET `ScriptName`='boss_maexxna' WHERE `entry`=15952; +UPDATE `creature_template` SET `ScriptName`='boss_noth' WHERE `entry`=15954; +UPDATE `creature_template` SET `ScriptName`='boss_heigan' WHERE `entry`=15936; +UPDATE `creature_template` SET `ScriptName`='boss_loatheb' WHERE `entry`=16011; +UPDATE `creature_template` SET `ScriptName`='boss_razuvious' WHERE `entry`=16061; +UPDATE `creature_template` SET `ScriptName`='boss_gothik' WHERE `entry`=16060; +UPDATE `creature_template` SET `ScriptName`='boss_highlord_mograine' WHERE `entry`=16062; +UPDATE `creature_template` SET `ScriptName`='boss_thane_korthazz' WHERE `entry`=16064; +UPDATE `creature_template` SET `ScriptName`='boss_sir_zeliek' WHERE `entry`=16063; +UPDATE `creature_template` SET `ScriptName`='boss_lady_blaumeux' WHERE `entry`=16065; +UPDATE `creature_template` SET `ScriptName`='boss_patchwerk' WHERE `entry`=16028; +UPDATE `creature_template` SET `ScriptName`='boss_grobbulus' WHERE `entry`=15931; +UPDATE `creature_template` SET `ScriptName`='boss_gluth' WHERE `entry`=15932; +UPDATE `creature_template` SET `ScriptName`='boss_thaddius' WHERE `entry`=15928; +UPDATE `creature_template` SET `ScriptName`='boss_stalagg' WHERE `entry`=15929; +UPDATE `creature_template` SET `ScriptName`='boss_fugen' WHERE `entry`=15930; +UPDATE `creature_template` SET `ScriptName`='boss_sapphiron' WHERE `entry`=15989; +UPDATE `creature_template` SET `ScriptName`='boss_kelthuzad' WHERE `entry`=15990; + +/* NETHERSTORM */ +UPDATE `gameobject_template` SET `ScriptName`='go_manaforge_control_console' WHERE `entry` IN (183770,183956,184311,184312); +UPDATE `creature_template` SET `ScriptName`='npc_manaforge_control_console' WHERE `entry` IN (20209,20417,20418,20440); +UPDATE `creature_template` SET `ScriptName`='npc_protectorate_nether_drake' WHERE `entry`=20903; +UPDATE `creature_template` SET `ScriptName`='npc_veronia' WHERE `entry`=20162; + +/* ONYXIA'S LAIR */ +UPDATE `creature_template` SET `ScriptName`='boss_onyxia' WHERE `entry`=10184; + +/* ORGRIMMAR */ +UPDATE `creature_template` SET `ScriptName`='npc_neeru_fireblade' WHERE `entry`=3216; +UPDATE `creature_template` SET `ScriptName`='npc_shenthul' WHERE `entry`=3401; +UPDATE `creature_template` SET `ScriptName`='npc_thrall_warchief' WHERE `entry`=4949; + +/* RAGEFIRE CHASM */ + + +/* RAZORFEN DOWNS */ +UPDATE `creature_template` SET `ScriptName`='boss_amnennar_the_coldbringer' WHERE `entry`=7358; + +/* REDRIDGE MOUNTAINS */ + + +/* RUINS OF AHN'QIRAJ */ +UPDATE `instance_template` SET `script`='instance_ruins_of_ahnqiraj' WHERE `map`=509; + +/* SCARLET MONASTERY */ +UPDATE `creature_template` SET `ScriptName`='boss_arcanist_doan' WHERE `entry`=6487; +UPDATE `creature_template` SET `ScriptName`='boss_azshir_the_sleepless' WHERE `entry`=6490; +UPDATE `creature_template` SET `ScriptName`='boss_bloodmage_thalnos' WHERE `entry`=4543; +UPDATE `creature_template` SET `ScriptName`='boss_herod' WHERE `entry`=3975; +UPDATE `creature_template` SET `ScriptName`='boss_high_inquisitor_fairbanks' WHERE `entry`=4542; +UPDATE `creature_template` SET `ScriptName`='boss_high_inquisitor_whitemane' WHERE `entry`=3977; +UPDATE `creature_template` SET `ScriptName`='boss_houndmaster_loksey' WHERE `entry`=3974; +UPDATE `creature_template` SET `ScriptName`='boss_interrogator_vishas' WHERE `entry`=3983; +UPDATE `creature_template` SET `ScriptName`='boss_scarlet_commander_mograine' WHERE `entry`=3976; +UPDATE `creature_template` SET `ScriptName`='boss_scorn' WHERE `entry`=14693; + +/* SCHOLOMANCE */ +UPDATE `instance_template` SET `script`='instance_scholomance' WHERE `map`=289; +UPDATE `creature_template` SET `ScriptName`='boss_darkmaster_gandling' WHERE `entry`=1853; +UPDATE `creature_template` SET `ScriptName`='boss_death_knight_darkreaver' WHERE `entry`=14516; +UPDATE `creature_template` SET `ScriptName`='boss_lord_alexei_barov' WHERE `entry`=10504; +UPDATE `creature_template` SET `ScriptName`='boss_instructor_malicia' WHERE `entry`=10505; +UPDATE `creature_template` SET `ScriptName`='boss_boss_ras_frostwhisper' WHERE `entry`=10508; +UPDATE `creature_template` SET `ScriptName`='boss_the_ravenian' WHERE `entry`=10507; +UPDATE `creature_template` SET `ScriptName`='boss_vectus' WHERE `entry`=10432; +UPDATE `creature_template` SET `ScriptName`='boss_illucia_barov' WHERE `entry`=10502; +UPDATE `creature_template` SET `ScriptName`='boss_doctor_theolen_krastinov' WHERE `entry`=11261; +UPDATE `creature_template` SET `ScriptName`='boss_jandice_barov' WHERE `entry`=10503; +UPDATE `creature_template` SET `ScriptName`='boss_lorekeeper_polkelt' WHERE `entry`=10901; +UPDATE `creature_template` SET `ScriptName`='boss_kormok' WHERE `entry`=16118; +UPDATE `creature_template` SET `ScriptName`='mob_illusionofjandicebarov' WHERE `entry`=11439; + +/* SEARING GORGE */ +UPDATE `creature_template` SET `ScriptName`='npc_kalaran_windblade' WHERE `entry`=8479; +UPDATE `creature_template` SET `ScriptName`='npc_lothos_riftwaker' WHERE `entry`=14387; +UPDATE `creature_template` SET `ScriptName`='npc_zamael_lunthistle' WHERE `entry`=8436; + +/* SHADOWFANG KEEP */ +UPDATE `instance_template` SET `script`='instance_shadowfang_keep' WHERE `map`=33; +UPDATE `creature_template` SET `ScriptName`='npc_shadowfang_prisoner' WHERE `entry` IN (3849,3850); + +/* SHADOWMOON VALLEY */ +UPDATE `creature_template` SET `ScriptName`='boss_doomwalker' WHERE `entry`=17711; +UPDATE `creature_template` SET `ScriptName`='npc_drake_dealer_hurlunk' WHERE `entry`=23489; +UPDATE `creature_template` SET `ScriptName`='npc_invis_legion_teleporter' WHERE `entry`=21807; +UPDATE `creature_template` SET `ScriptName`='npcs_flanis_swiftwing_and_kagrosh' WHERE `entry` IN (21725,21727); +UPDATE `creature_template` SET `ScriptName`='npc_murkblood_overseer' WHERE `entry`=23309; +UPDATE `creature_template` SET `ScriptName`='npc_neltharaku' WHERE `entry`=21657; +UPDATE `creature_template` SET `ScriptName`='npc_oronok_tornheart' WHERE `entry`=21183; +UPDATE `creature_template` SET `ScriptName`='mob_mature_netherwing_drake' WHERE `entry`=21648; +UPDATE `creature_template` SET `ScriptName`='mob_enslaved_netherwing_drake' WHERE `entry`=21722; + +/* SHATTRATH */ +UPDATE `creature_template` SET `ScriptName`='npc_raliq_the_drunk' WHERE `entry`=18585; +UPDATE `creature_template` SET `ScriptName`='npc_salsalabim' WHERE `entry`=18584; +UPDATE `creature_template` SET `ScriptName`='npc_shattrathflaskvendors' WHERE `entry` IN (23483,23484); +UPDATE `creature_template` SET `ScriptName`='npc_zephyr' WHERE `entry`=25967; + +/* SILITHUS */ +UPDATE `creature_template` SET `ScriptName`='npcs_rutgar_and_frankal' WHERE `entry` IN (15170,15171); + +/* SILVERMOON */ +UPDATE `creature_template` SET `ScriptName`='npc_blood_knight_stillblade' WHERE `entry`=17768; + +/* SILVERPINE FOREST */ +UPDATE `creature_template` SET `ScriptName`='npc_astor_hadren' WHERE `entry`=6497; + +/* STOCKADES */ + +/* STONETALON MOUNTAINS */ +UPDATE `creature_template` SET `ScriptName`='npc_braug_dimspirit' WHERE `entry`=4489; + +/* STORMWIND CITY */ +UPDATE `creature_template` SET `ScriptName`='npc_archmage_malin' WHERE `entry`=2708; +UPDATE `creature_template` SET `ScriptName`='npc_bartleby' WHERE `entry`=6090; +UPDATE `creature_template` SET `ScriptName`='npc_dashel_stonefist' WHERE `entry`=4961; +UPDATE `creature_template` SET `ScriptName`='npc_general_marcus_jonathan' WHERE `entry`=466; +UPDATE `creature_template` SET `ScriptName`='npc_lady_katrana_prestor' WHERE `entry`=1749; + +/* STRANGLETHORN VALE */ +UPDATE `creature_template` SET `ScriptName`='mob_yenniku' WHERE `entry`=2530; + +/* STRATHOLME */ +UPDATE `instance_template` SET `script`='instance_stratholme' WHERE `map`=329; +UPDATE `creature_template` SET `ScriptName`='boss_dathrohan_balnazzar' WHERE `entry`=10812; +UPDATE `creature_template` SET `ScriptName`='boss_magistrate_barthilas' WHERE `entry`=10435; +UPDATE `creature_template` SET `ScriptName`='boss_maleki_the_pallid' WHERE `entry`=10438; +UPDATE `creature_template` SET `ScriptName`='boss_nerubenkan' WHERE `entry`=10437; +UPDATE `creature_template` SET `ScriptName`='boss_cannon_master_willey' WHERE `entry`=10997; +UPDATE `creature_template` SET `ScriptName`='boss_baroness_anastari' WHERE `entry`=10436; +UPDATE `creature_template` SET `ScriptName`='boss_ramstein_the_gorger' WHERE `entry`=10439; +UPDATE `creature_template` SET `ScriptName`='boss_timmy_the_cruel' WHERE `entry`=10808; +UPDATE `creature_template` SET `ScriptName`='boss_silver_hand_bosses' WHERE `entry` IN (17910,17911,17912,17913,17914); +UPDATE `creature_template` SET `ScriptName`='boss_postmaster_malown' WHERE `entry`=11143; +UPDATE `creature_template` SET `ScriptName`='boss_baron_rivendare' WHERE `entry`=10440; +UPDATE `creature_template` SET `ScriptName`='mob_mindless_skeleton' WHERE `entry`=11197; +UPDATE `creature_template` SET `ScriptName`='mob_thuzadin_acolyte' WHERE `entry`=10399; +UPDATE `creature_template` SET `ScriptName`='mobs_spectral_ghostly_citizen' WHERE `entry` IN (10384,10385); +UPDATE `creature_template` SET `ScriptName`='mob_restless_soul' WHERE `entry`=11122; +UPDATE `creature_template` SET `ScriptName`='mob_freed_soul' WHERE `entry`=11136; + +/* SUNKEN TEMPLE */ + + +/* SWAMP OF SORROWS */ + + +/* TANARIS */ +UPDATE `creature_template` SET `ScriptName`='mob_aquementas' WHERE `entry`=9453; +UPDATE `creature_template` SET `ScriptName`='npc_custodian_of_time' WHERE `entry`=20129; +UPDATE `creature_template` SET `ScriptName`='npc_marin_noggenfogger' WHERE `entry`=7564; +UPDATE `creature_template` SET `ScriptName`='npc_steward_of_time' WHERE `entry`=20142; +UPDATE `creature_template` SET `ScriptName`='npc_stone_watcher_of_norgannon' WHERE `entry`=7918; + +/* TELDRASSIL */ + + +/* */ +/* TEMPEST KEEP */ +/* */ + +/* THE MECHANAR */ +UPDATE `creature_template` SET `ScriptName`='boss_gatewatcher_iron_hand' WHERE `entry`=19710; +UPDATE `creature_template` SET `ScriptName`='boss_nethermancer_sepethrea' WHERE `entry`=19221; +UPDATE `creature_template` SET `ScriptName`='mob_ragin_flames' WHERE `entry`=20481; + +/* THE BOTANICA */ +UPDATE `creature_template` SET `ScriptName`='boss_high_botanist_freywinn' WHERE `entry`=17975; +UPDATE `creature_template` SET `ScriptName`='boss_laj' WHERE `entry`=17980; +UPDATE `creature_template` SET `ScriptName`='boss_warp_splinter' WHERE `entry`=17977; +UPDATE `creature_template` SET `ScriptName`='mob_warp_splinter_treant' WHERE `entry`=19949; + +/* THE ARCATRAZ */ +UPDATE `instance_template` SET `script`='instance_arcatraz' WHERE `map`=552; +UPDATE `creature_template` SET `ScriptName`='mob_zerekethvoidzone' WHERE `entry`=21101; +UPDATE `creature_template` SET `ScriptName`='boss_harbinger_skyriss' WHERE `entry`=20912; +UPDATE `creature_template` SET `ScriptName`='boss_harbinger_skyriss_illusion' WHERE `entry` IN (21466,21467); +UPDATE `creature_template` SET `ScriptName`='npc_warden_mellichar' WHERE `entry`=20904; +UPDATE `creature_template` SET `ScriptName`='npc_millhouse_manastorm' WHERE `entry`=20977; + +/* THE EYE */ +UPDATE `instance_template` SET `script`='instance_the_eye' WHERE `map`=550; +/* The Eye Trash Mobs */ +UPDATE `creature_template` SET `ScriptName`='mob_crystalcore_devastator' WHERE `entry`=20040; +/* Void Reaver event */ +UPDATE `creature_template` SET `ScriptName`='boss_void_reaver' WHERE `entry`=19516; +/* Astromancer event */ +UPDATE `creature_template` SET `ScriptName`='boss_high_astromancer_solarian' WHERE `entry`=18805; +UPDATE `creature_template` SET `ScriptName`='mob_solarium_priest' WHERE `entry`=18806; +/* Kael'thas event */ +UPDATE `creature_template` SET `ScriptName`='boss_kaelthas' WHERE `entry`=19622; +UPDATE `creature_template` SET `ScriptName`='boss_thaladred_the_darkener' WHERE `entry`=20064; +UPDATE `creature_template` SET `ScriptName`='boss_lord_sanguinar' WHERE `entry`=20060; +UPDATE `creature_template` SET `ScriptName`='boss_grand_astromancer_capernian' WHERE `entry`=20062; +UPDATE `creature_template` SET `ScriptName`='boss_master_engineer_telonicus' WHERE `entry`=20063; +UPDATE `creature_template` SET `ScriptName`='mob_phoenix' WHERE `entry`=21362; +UPDATE `creature_template` SET `ScriptName`='mob_nether_vapor' WHERE `entry`=21002; +UPDATE `creature_template` SET `ScriptName`='mob_kael_flamestrike' WHERE `entry`=21369; + +/* TEMPLE OF AHN'QIRAJ */ +UPDATE `instance_template` SET `script`='instance_temple_of_ahnqiraj' WHERE `map`=531; +UPDATE `creature_template` SET `ScriptName`='boss_cthun' WHERE `entry`=15727; +UPDATE `creature_template` SET `ScriptName`='boss_skeram' WHERE `entry`=15263; +UPDATE `creature_template` SET `ScriptName`='boss_vem' WHERE `entry`=15544; +UPDATE `creature_template` SET `ScriptName`='boss_yauj' WHERE `entry`=15543; +UPDATE `creature_template` SET `ScriptName`='boss_kri' WHERE `entry`=15511; +UPDATE `creature_template` SET `ScriptName`='boss_sartura' WHERE `entry`=15516; +UPDATE `creature_template` SET `ScriptName`='boss_fankriss' WHERE `entry`=15510; +UPDATE `creature_template` SET `ScriptName`='boss_viscidus' WHERE `entry`=15299; +UPDATE `creature_template` SET `ScriptName`='boss_glob_of_viscidus' WHERE `entry`=15667; +UPDATE `creature_template` SET `ScriptName`='boss_huhuran' WHERE `entry`=15509; +UPDATE `creature_template` SET `ScriptName`='boss_veklor' WHERE `entry`=15276; +UPDATE `creature_template` SET `ScriptName`='boss_veknilash' WHERE `entry`=15275; +UPDATE `creature_template` SET `ScriptName`='boss_ouro' WHERE `entry`=15517; +UPDATE `creature_template` SET `ScriptName`='boss_eye_of_cthun' WHERE `entry`=15589; +UPDATE `creature_template` SET `ScriptName`='mob_sartura_royal_guard' WHERE `entry`=15984; +UPDATE `creature_template` SET `ScriptName`='mob_yauj_brood' WHERE `entry`=15621; +UPDATE `creature_template` SET `ScriptName`='mob_claw_tentacle' WHERE `entry`=15725; +UPDATE `creature_template` SET `ScriptName`='mob_eye_tentacle' WHERE `entry`=15726; +UPDATE `creature_template` SET `ScriptName`='mob_giant_claw_tentacle' WHERE `entry`=15728; +UPDATE `creature_template` SET `ScriptName`='mob_giant_eye_tentacle' WHERE `entry`=15334; +UPDATE `creature_template` SET `ScriptName`='mob_giant_flesh_tentacle' WHERE `entry`=15802; +UPDATE `creature_template` SET `ScriptName`='mob_anubisath_sentinel' WHERE `entry`=15264; + +/* TEROKKAR FOREST */ +UPDATE `creature_template` SET `ScriptName`='mob_infested_root_walker' WHERE `entry`=22095; +UPDATE `creature_template` SET `ScriptName`='mob_netherweb_victim' WHERE `entry`=22355; +UPDATE `creature_template` SET `ScriptName`='mob_rotting_forest_rager' WHERE `entry`=22307; +UPDATE `creature_template` SET `ScriptName`='npc_floon' WHERE `entry`=18588; +UPDATE `creature_template` SET `ScriptName`='npc_skyguard_handler_irena' WHERE `entry`=23413; + +/* THOUSAND NEEDLES */ + + +/* THUNDER BLUFF */ +UPDATE `creature_template` SET `ScriptName`='npc_cairne_bloodhoof' WHERE `entry`=3057; + +/* TIRISFAL GLADES */ +UPDATE `creature_template` SET `ScriptName`='npc_calvin_montague' WHERE `entry`=6784; + +/* ULDAMAN */ +UPDATE `creature_template` SET `ScriptName`='boss_ironaya' WHERE `entry`=7228; +UPDATE `creature_template` SET `ScriptName`='mob_jadespine_basilisk' WHERE `entry`=4863; +UPDATE `creature_template` SET `ScriptName`='npc_lore_keeper_of_norgannon' WHERE `entry`=7172; + +/* UN'GORO CRATER */ + + +/* UNDERCITY */ +UPDATE `creature_template` SET `ScriptName`='npc_lady_sylvanas_windrunner' WHERE `entry`=10181; +UPDATE `creature_template` SET `ScriptName`='npc_highborne_lamenter' WHERE `entry`=21628; +UPDATE `creature_template` SET `ScriptName`='npc_parqual_fintallas' WHERE `entry`=4488; + +/* WAILING CAVERNS */ + + +/* WESTERN PLAGUELANDS */ +UPDATE `creature_template` SET `ScriptName`='npcs_dithers_and_arbington' WHERE `entry` IN (11056,11057); +UPDATE `creature_template` SET `ScriptName`='npc_the_scourge_cauldron' WHERE `entry`=11152; + +/* WESTFALL */ + + +/* WETLANDS */ + + +/* WINTERSPRING */ +UPDATE `creature_template` SET `ScriptName`='npc_lorax' WHERE `entry`=10918; +UPDATE `creature_template` SET `ScriptName`='npc_rivern_frostwind' WHERE `entry`=10618; +UPDATE `creature_template` SET `ScriptName`='npc_witch_doctor_mauari' WHERE `entry`=10307; + +/* ZANGARMARSH */ +UPDATE `creature_template` SET `ScriptName`='npcs_ashyen_and_keleth' WHERE `entry` IN (17900,17901); +UPDATE `creature_template` SET `ScriptName`='npc_cooshcoosh' WHERE `entry`=18586; +UPDATE `creature_template` SET `ScriptName`='npc_elder_kuruti' WHERE `entry`=18197; +UPDATE `creature_template` SET `ScriptName`='npc_mortog_steamhead' WHERE `entry`=23373; + +/* ZUL'AMAN */ +UPDATE `instance_template` SET `script`='instance_zulaman' WHERE `map`=568; +UPDATE `creature_template` SET `ScriptName`='boss_janalai' WHERE `entry`=23578; +UPDATE `creature_template` SET `ScriptName`='boss_nalorakk' WHERE `entry`=23576; +UPDATE `creature_template` SET `ScriptName`='mob_jandalai_firebomb' WHERE `entry`=23920; +UPDATE `creature_template` SET `ScriptName`='mob_amanishi_hatcher' WHERE `entry`=23818; +UPDATE `creature_template` SET `ScriptName`='mob_hatchling' WHERE `entry`=23598; +UPDATE `creature_template` SET `ScriptName`='npc_forest_frog' WHERE `entry`=24396; + +/* ZUL'FARRAK */ +UPDATE `creature_template` SET `ScriptName`='npc_sergeant_bly' WHERE `entry`=7604; +UPDATE `creature_template` SET `ScriptName`='npc_weegli_blastfuse' WHERE `entry`=7607; + +/* ZUL'GURUB */ +UPDATE `instance_template` SET `script`='instance_zulgurub' WHERE `map`=309; +UPDATE `creature_template` SET `ScriptName`='boss_jeklik' WHERE `entry`=14517; +UPDATE `creature_template` SET `ScriptName`='boss_venoxis' WHERE `entry`=14507; +UPDATE `creature_template` SET `ScriptName`='boss_marli' WHERE `entry`=14510; +UPDATE `creature_template` SET `ScriptName`='boss_mandokir' WHERE `entry`=11382; +UPDATE `creature_template` SET `ScriptName`='boss_gahzranka' WHERE `entry`=15114; +UPDATE `creature_template` SET `ScriptName`='boss_jindo' WHERE `entry`=11380; +UPDATE `creature_template` SET `ScriptName`='boss_hakkar' WHERE `entry`=14834; +UPDATE `creature_template` SET `ScriptName`='boss_thekal' WHERE `entry`=14509; +UPDATE `creature_template` SET `ScriptName`='boss_arlokk' WHERE `entry`=14515; +UPDATE `creature_template` SET `ScriptName`='boss_grilek' WHERE `entry`=15082; +UPDATE `creature_template` SET `ScriptName`='boss_hazzarah' WHERE `entry`=15083; +UPDATE `creature_template` SET `ScriptName`='boss_renataki' WHERE `entry`=15084; +UPDATE `creature_template` SET `ScriptName`='boss_wushoolay' WHERE `entry`=15085; +UPDATE `creature_template` SET `ScriptName`='mob_zealot_lorkhan' WHERE `entry`=11347; +UPDATE `creature_template` SET `ScriptName`='mob_zealot_zath' WHERE `entry`=11348; +UPDATE `creature_template` SET `ScriptName`='mob_healing_ward' WHERE `entry`=14987; +UPDATE `creature_template` SET `ScriptName`='mob_spawn_of_marli' WHERE `entry`=15041; +UPDATE `creature_template` SET `ScriptName`='mob_batrider' WHERE `entry`=14965; +UPDATE `creature_template` SET `ScriptName`='mob_shade_of_jindo' WHERE `entry`=14986; + +/* EOF */ diff --git a/src/bindings/scripts/sql/old/mangos_old_spells.sql b/src/bindings/scripts/sql/old/mangos_old_spells.sql new file mode 100644 index 00000000000..8419cacd226 --- /dev/null +++ b/src/bindings/scripts/sql/old/mangos_old_spells.sql @@ -0,0 +1,374 @@ +/* Old spell queries from Scriptdev revision 55. Not supported by scriptdev anylonger */ +/* To be used in ordinary database developement. Can be added in any changeset/revision as database developers sees fit */ + +UPDATE `creature_template` SET `spell1` = 116 WHERE `entry` = 946; +UPDATE `creature_template` SET `spell1` = 16415 WHERE `entry` = 4063; +UPDATE `creature_template` SET `spell1` = 205 WHERE `entry` = 1867; +UPDATE `creature_template` SET `spell1` = 705 WHERE `entry` = 1915; +UPDATE `creature_template` SET `spell1` = 145, `spell2` = 134 WHERE `entry` = 1914; +UPDATE `creature_template` SET `spell1` = 837, `spell2` = 122 WHERE `entry` = 1889; +UPDATE `creature_template` SET `spell1` = 8406, `spell2` = 865 WHERE `entry` = 314; +UPDATE `creature_template` SET `spell1` = 6041, `spell2` = 6364 WHERE `entry` = 2570; +UPDATE `creature_template` SET `spell1` = 8407, `spell2` = 12486 WHERE `entry` = 2567; +UPDATE `creature_template` SET `spell1` = 6041 WHERE `entry` = 697; +UPDATE `creature_template` SET `spell1` = 8402, `spell2` = 8423 WHERE `entry` = 1653; +UPDATE `creature_template` SET `spell1` = 8402 WHERE `entry` = 1562; +UPDATE `creature_template` SET `spell1` = 7641, `spell2` = 11707 WHERE `entry` = 1564; +UPDATE `creature_template` SET `spell1` = 1106 WHERE `entry` = 3218; +UPDATE `creature_template` SET `spell1` = 837 WHERE `entry` = 2591; +UPDATE `creature_template` SET `spell1` = 837 WHERE `entry` = 2255; +UPDATE `creature_template` SET `spell1` = 205 WHERE `entry` = 1539; +UPDATE `creature_template` SET `spell1` = 15264, `spell2` = 6060, `spell3` = 988 WHERE `entry` = 4299; +UPDATE `creature_template` SET `spell1` = 984, `spell2` = 1026 WHERE `entry` = 4296; +UPDATE `creature_template` SET `spell1` = 8438, `spell2` = 2601 WHERE `entry` = 4300; +UPDATE `creature_template` SET `spell1` = 1088, `spell2` = 992 WHERE `entry` = 533; +UPDATE `creature_template` SET `spell1` = 7322 WHERE `entry` = 203; +UPDATE `creature_template` SET `spell1` = 1106, `spell2` = 2941 WHERE `entry` = 2577; +UPDATE `creature_template` SET `spell1` = 548, `spell2` = 6535 WHERE `entry` = 3273; +UPDATE `creature_template` SET `spell1` = 529 WHERE `entry` = 1183; +UPDATE `creature_template` SET `spell1` = 992, `spell2` = 705 WHERE `entry` = 436; +UPDATE `creature_template` SET `spell1` = 5177 WHERE `entry` = 7235; +UPDATE `creature_template` SET `spell1` = 5177 WHERE `entry` = 2012; +UPDATE `creature_template` SET `spell1` = 403, `spell2` = 770 WHERE `entry` = 1397; +UPDATE `creature_template` SET `spell1` = 2136 WHERE `entry` = 1174; +UPDATE `creature_template` SET `spell1` = 143 WHERE `entry` = 2018; +UPDATE `creature_template` SET `spell1` = 915, `spell2` = 2606 WHERE `entry` = 1399; +UPDATE `creature_template` SET `spell1` = 915, `spell2` = 8293 WHERE `entry` = 3783; +UPDATE `creature_template` SET `spell1` = 984 WHERE `entry` = 3732; +UPDATE `creature_template` SET `spell1` = 13480 WHERE `entry` = 3725; +UPDATE `creature_template` SET `spell1` = 13480 WHERE `entry` = 3728; +UPDATE `creature_template` SET `spell1` = 9672, `spell2` = 7101 WHERE `entry` = 3662; +UPDATE `creature_template` SET `spell1` = 18089 WHERE `entry` = 2207; +UPDATE `creature_template` SET `spell1` = 8402, `spell2` = 10215 WHERE `entry` = 7026; +UPDATE `creature_template` SET `spell1` = 11659, `spell2` = 8402 WHERE `entry` = 7028; +UPDATE `creature_template` SET `spell1` = 7918, `spell2` = 6685 WHERE `entry` = 7038; +UPDATE `creature_template` SET `spell1` = 676, `spell2` = 53, `spell3` = 0 WHERE `entry` = 6866; +UPDATE `creature_template` SET `spell1` = 53, `spell2` = 0, `spell3` = 0 WHERE `entry` = 122; +UPDATE `creature_template` SET `spell1` = 71, `spell2` = 1671 WHERE `entry` = 449; +UPDATE `creature_template` SET `spell1` = 6554 WHERE `entry` = 121; +UPDATE `creature_template` SET `spell1` = 53 WHERE `entry` = 590; +UPDATE `creature_template` SET `spell1` = 12166 WHERE `entry` = 3232; +UPDATE `creature_template` SET `spell1` = 403 WHERE `entry` = 2953; +UPDATE `creature_template` SET `spell1` = 3385 WHERE `entry` = 2954; +UPDATE `creature_template` SET `spell1` = 15657, `spell2` = 17230, `spell3` = 16509, `spell4` = 0 WHERE `entry` = 1707; +UPDATE `creature_template` SET `spell1` = 6253, `spell2` = 16244, `spell3` = 8242, `spell4` = 16509 WHERE `entry` = 1711; +UPDATE `creature_template` SET `spell1` = 6547, `spell2` = 2590, `spell3` = 6253 WHERE `entry` = 1708; +UPDATE `creature_template` SET `spell1` = 11554, `spell2` = 8242, `spell3` = 6253 WHERE `entry` = 1715; +UPDATE `creature_template` SET `spell1` = 1768, `spell2` = 17230, `spell3` = 16509 WHERE `entry` = 1706; +UPDATE `creature_template` SET `spell1` = 5115 WHERE `entry` = 1729; +UPDATE `creature_template` SET `spell1` = 9915 WHERE `entry` = 1732; +UPDATE `creature_template` SET `spell1` = 143 WHERE `entry` = 1726; +UPDATE `creature_template` SET `spell1` = 143 WHERE `entry` = 619; +UPDATE `creature_template` SET `spell1` = 6660 WHERE `entry` = 657; +UPDATE `creature_template` SET `spell1` = 6660, `spell2` = 6685 WHERE `entry` = 4417; +UPDATE `creature_template` SET `spell1` = 6685 WHERE `entry` = 598; +UPDATE `creature_template` SET `spell1` = 6016 WHERE `entry` = 4416; +UPDATE `creature_template` SET `spell1` = 5115, `spell2` = 6435 WHERE `entry` = 594; +UPDATE `creature_template` SET `spell1` = 9915 WHERE `entry` = 634; +UPDATE `creature_template` SET `spell1` = 205 WHERE `entry` = 1725; +UPDATE `creature_template` SET `spell1` = 133, `spell2` = 205, `spell3` = 113 WHERE `entry` = 4418; +UPDATE `creature_template` SET `spell1` = 6136, `spell2` = 116 WHERE `entry` = 474; +UPDATE `creature_template` SET `spell1` = 3140, `spell2` = 12486, `spell3` = 3443 WHERE `entry` = 910; +UPDATE `creature_template` SET `spell1` = 744, `spell2` = 7992, `spell3` = 2590 WHERE `entry` = 909; +UPDATE `creature_template` SET `spell1` = 8646 WHERE `entry` = 116; +UPDATE `creature_template` SET `spell1` = 53 WHERE `entry` = 504; +UPDATE `creature_template` SET `spell1` = 2764 WHERE `entry` = 95; +UPDATE `creature_template` SET `spell1` = 53, `spell2` = 133 WHERE `entry` = 94; +UPDATE `creature_template` SET `spell1` = 168 WHERE `entry` = 589; +UPDATE `creature_template` SET `spell1` = 8382, `spell2` = 8733 WHERE `entry` = 4819; +UPDATE `creature_template` SET `spell1` = 6145, `spell2` = 71, `spell3` = 22691 WHERE `entry` = 4818; +UPDATE `creature_template` SET `spell1` = 14109, `spell2` = 3358, `spell3` = 8733, `spell4` = 12024 WHERE `entry` = 4820; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 4359; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 1024; +UPDATE `creature_template` SET `spell1` = 1777, `spell2` = 6533 WHERE `entry` = 1028; +UPDATE `creature_template` SET `spell1` = 10177 WHERE `entry` = 1418; +UPDATE `creature_template` SET `spell1` = 744, `spell2` = 1707 WHERE `entry` = 1026; +UPDATE `creature_template` SET `spell1` = 7372, `spell2` = 2457 WHERE `entry` = 1027; +UPDATE `creature_template` SET `spell1` = 15869 WHERE `entry` = 1029; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 747; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 171; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 544; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 545; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 578; +UPDATE `creature_template` SET `spell1` = 744, `spell2` = 865 WHERE `entry` = 127; +UPDATE `creature_template` SET `spell1` = 13519, `spell2` = 6074 WHERE `entry` = 517; +UPDATE `creature_template` SET `spell1` = 10277 WHERE `entry` = 458; +UPDATE `creature_template` SET `spell1` = 7357 WHERE `entry` = 126; +UPDATE `creature_template` SET `spell1` = 205, `spell2` = 331 WHERE `entry` = 548; +UPDATE `creature_template` SET `spell1` = 2589 WHERE `entry` = 732; +UPDATE `creature_template` SET `spell1` = 3368 WHERE `entry` = 46; +UPDATE `creature_template` SET `spell1` = 6268 WHERE `entry` = 1083; +UPDATE `creature_template` SET `spell1` = 3393 WHERE `entry` = 422; +UPDATE `creature_template` SET `spell1` = 3288 WHERE `entry` = 1011; +UPDATE `creature_template` SET `spell1` = 8016 WHERE `entry` = 1008; +UPDATE `creature_template` SET `spell1` = 8016 WHERE `entry` = 1007; +UPDATE `creature_template` SET `spell1` = 205 WHERE `entry` = 1009; +UPDATE `creature_template` SET `spell1` = 547, `spell2` = 548 WHERE `entry` = 1013; +UPDATE `creature_template` SET `spell1` = 744 WHERE `entry` = 434; +UPDATE `creature_template` SET `spell1` = 3150 WHERE `entry` = 433; +UPDATE `creature_template` SET `spell1` = 6205 WHERE `entry` = 432; +UPDATE `creature_template` SET `spell1` = 705 WHERE `entry` = 429; +UPDATE `creature_template` SET `spell1` = 71, `spell2` = 8380, `spell3` = 8629 WHERE `entry` = 568; +UPDATE `creature_template` SET `spell1` = 744 WHERE `entry` = 579; +UPDATE `creature_template` SET `spell1` = 6730, `spell2` = 6016 WHERE `entry` = 448; +UPDATE `creature_template` SET `spell1` = 6730, `spell2` = 6016 WHERE `entry` = 500; +UPDATE `creature_template` SET `spell1` = 8016, `spell2` = 0 WHERE `entry` = 123; +UPDATE `creature_template` SET `spell1` = 1160 WHERE `entry` = 124; +UPDATE `creature_template` SET `spell1` = 3368 WHERE `entry` = 501; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1674; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1772; +UPDATE `creature_template` SET `spell1` = 3237, `spell2` = 695 WHERE `entry` = 1773; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1939; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1940; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1942; +UPDATE `creature_template` SET `spell1` = 3237 WHERE `entry` = 1944; +UPDATE `creature_template` SET `spell1` = 2480 WHERE `entry` = 3112; +UPDATE `creature_template` SET `spell1` = 5280 WHERE `entry` = 3111; +UPDATE `creature_template` SET `spell1` = 3248 WHERE `entry` = 3114; +UPDATE `creature_template` SET `spell1` = 6950, `spell2` = 774 WHERE `entry` = 3113; +UPDATE `creature_template` SET `spell1` = 548, `spell2` = 8045, `spell3` = 8052 WHERE `entry` = 3269; +UPDATE `creature_template` SET `spell1` = 594, `spell2` = 8092, `spell3` = 2052 WHERE `entry` = 3458; +UPDATE `creature_template` SET `spell1` = 594, `spell2` = 598, `spell3` = 8102, `spell4` = 6074 WHERE `entry` = 3271; +UPDATE `creature_template` SET `spell1` = 1978, `spell2` = 3044 WHERE `entry` = 3265; +UPDATE `creature_template` SET `spell1` = 13446, `spell2` = 12323, `spell3` = 13443 WHERE `entry` = 3459; +UPDATE `creature_template` SET `spell1` = 22356 WHERE `entry` = 3267; +UPDATE `creature_template` SET `spell1` = 3427 WHERE `entry` = 712; +UPDATE `creature_template` SET `spell1` = 5164 WHERE `entry` = 446; +UPDATE `creature_template` SET `spell1` = 331, `spell2` = 548 WHERE `entry` = 430; +UPDATE `creature_template` SET `spell1` = 7919 WHERE `entry` = 424; +UPDATE `creature_template` SET `spell1` = 3148 WHERE `entry` = 1115; +UPDATE `creature_template` SET `spell1` = 5164 WHERE `entry` = 1117; +UPDATE `creature_template` SET `spell1` = 53 WHERE `entry` = 1116; +UPDATE `creature_template` SET `spell1` = 5164 WHERE `entry` = 1118; +UPDATE `creature_template` SET `spell1` = 10277 WHERE `entry` = 1162; +UPDATE `creature_template` SET `spell1` = 529 WHERE `entry` = 1166; +UPDATE `creature_template` SET `spell1` = 1776 WHERE `entry` = 1163; +UPDATE `creature_template` SET `spell1` = 11976 WHERE `entry` = 1164; +UPDATE `creature_template` SET `spell1` = 331 WHERE `entry` = 1197; +UPDATE `creature_template` SET `spell1` = 2121 WHERE `entry` = 1165; +UPDATE `creature_template` SET `spell1` = 7405, `spell2` = 71 WHERE `entry` = 1167; +UPDATE `creature_template` SET `spell1` = 11660, `spell2` = 17926, `spell3` = 6215, `spell4` = 11672 WHERE `entry` = 1789; +UPDATE `creature_template` SET `spell1` = 348, `spell2` = 686 WHERE `entry` = 4473; +UPDATE `creature_template` SET `spell1` = 15580, `spell2` = 15613, `spell3` = 23600, `spell4` = 16406 WHERE `entry` = 10391; +UPDATE `creature_template` SET `spell1` = 3416 WHERE `entry` = 531; +UPDATE `creature_template` SET `spell1` = 7369 WHERE `entry` = 1783; +UPDATE `creature_template` SET `spell1` = 838 WHERE `entry` = 7341; +UPDATE `creature_template` SET `spell1` = 15580, `spell2` = 15613, `spell3` = 16406 WHERE `entry` = 10390; +UPDATE `creature_template` SET `spell1` = 9734, `spell2` = 6074 WHERE `entry` = 787; +UPDATE `creature_template` SET `spell1` = 7322, `spell2` = 12486 WHERE `entry` = 203; +UPDATE `creature_template` SET `spell1` = 7992 WHERE `entry` = 1110; +UPDATE `creature_template` SET `spell1` = 17312, `spell2` = 992, `spell3` = 984 WHERE `entry` = 7340; +UPDATE `creature_template` SET `spell1` = 22940, `spell2` = 1411 WHERE `entry` = 1784; +UPDATE `creature_template` SET `spell1` = 705, `spell2` = 1014 WHERE `entry` = 7342; +UPDATE `creature_template` SET `spell1` = 8699 WHERE `entry` = 785; +UPDATE `creature_template` SET `spell1` = 7373 WHERE `entry` = 48; +UPDATE `creature_template` SET `spell1` = 19644, `spell2` = 21949, `spell3` = 19642 WHERE `entry` = 6488; +UPDATE `creature_template` SET `spell1` = 10179, `spell2` = 22645, `spell3` = 13009, `spell4` = 12556 WHERE `entry` = 7358; +UPDATE `creature_template` SET `spell1` = 10148, `spell2` = 12470 WHERE `entry` = 7357; +UPDATE `creature_template` SET `spell1` = 12947, `spell2` = 12946 WHERE `entry` = 7356; +UPDATE `creature_template` SET `spell1` = 12255, `spell2` = 12252, `spell3` = 12254 WHERE `entry` = 7355; +UPDATE `creature_template` SET `spell1` = 10892, `spell2` = 11659 WHERE `entry` = 7354; +UPDATE `creature_template` SET `spell1` = 21667, `spell2` = 21331, `spell3` = 21793 WHERE `entry` = 12225; +UPDATE `creature_template` SET `spell1` = 21080, `spell2` = 8817 WHERE `entry` = 12236; +UPDATE `creature_template` SET `spell1` = 24375, `spell2` = 15580 WHERE `entry` = 12237; +UPDATE `creature_template` SET `spell1` = 21909, `spell2` = 21832, `spell3` = 19128, `spell4` = 21869 WHERE `entry` = 12201; +UPDATE `creature_template` SET `spell1` = 21911, `spell2` = 15584, `spell3` = 21749 WHERE `entry` = 12258; +UPDATE `creature_template` SET `spell1` = 21833, `spell2` = 22334 WHERE `entry` = 13601; +UPDATE `creature_template` SET `spell1` = 21687, `spell2` = 21547, `spell3` = 21707 WHERE `entry` = 13282; +UPDATE `creature_template` SET `spell1` = 9770, `spell2` = 16145, `spell3` = 1604, `spell4` = 9776 WHERE `entry` = 6206; +UPDATE `creature_template` SET `spell1` = 9770, `spell2` = 1604, `spell3` = 12540 WHERE `entry` = 6208; +UPDATE `creature_template` SET `spell1` = 10851 WHERE `entry` = 6209; +UPDATE `creature_template` SET `spell1` = 1604, `spell2` = 6409, `spell3` = 3420 WHERE `entry` = 6215; +UPDATE `creature_template` SET `spell1` = 9459, `spell2` = 1604, `spell3` = 10341, `spell4` = 11638 WHERE `entry` = 6219; +UPDATE `creature_template` SET `spell1` = 16169, `spell2` = 5568, `spell3` = 10887 WHERE `entry` = 6229; +UPDATE `creature_template` SET `spell1` = 3053, `spell2` = 16412 WHERE `entry` = 6228; +UPDATE `creature_template` SET `spell1` = 11082, `spell2` = 11085, `spell3` = 11084 WHERE `entry` = 6235; +UPDATE `creature_template` SET `spell1` = 1604 WHERE `entry` = 7361; +UPDATE `creature_template` SET `spell1` = 6660, `spell2` = 8858 WHERE `entry` = 6407; +UPDATE `creature_template` SET `spell1` = 8211, `spell2` = 10341, `spell3` = 1604, `spell4` = 9459 WHERE `entry` = 6220; +UPDATE `creature_template` SET `spell1` = 10341, `spell2` = 1604, `spell3` = 9459 WHERE `entry` = 6218; +UPDATE `creature_template` SET `spell1` = 1604, `spell2` = 11264, `spell3` = 12024 WHERE `entry` = 7603; +UPDATE `creature_template` SET `spell1` = 6660, `spell2` = 2643, `spell3` = 5116 WHERE `entry` = 6223; +UPDATE `creature_template` SET `spell1` = 13398, `spell2` = 1604, `spell3` = 12024 WHERE `entry` = 6222; +UPDATE `creature_template` SET `spell1` = 11820, `spell2` = 1604 WHERE `entry` = 6234; +UPDATE `creature_template` SET `spell1` = 1604 WHERE `entry` = 6233; +UPDATE `creature_template` SET `spell1` = 11306, `spell2` = 10733 WHERE `entry` = 6226; +UPDATE `creature_template` SET `spell1` = 22519, `spell2` = 11264 WHERE `entry` = 6227; +UPDATE `creature_template` SET `spell1` = 10346, `spell2` = 1604, `spell3` = 17174 WHERE `entry` = 6225; +UPDATE `creature_template` SET `spell1` = 6533, `spell2` = 1604, `spell3` = 11820, `spell4` = 11084 WHERE `entry` = 6230; +UPDATE `creature_template` SET `spell1` = 10341, `spell2` = 9459 WHERE `entry` = 7079; +UPDATE `creature_template` SET `spell1` = 22689, `spell2` = 22662, `spell3` = 19319 WHERE `entry` = 11492; +UPDATE `creature_template` SET `spell1` = 15708, `spell2` = 23511 WHERE `entry` = 14325; +UPDATE `creature_template` SET `spell1` = 10947, `spell2` = 10151 WHERE `entry` = 14324; +UPDATE `creature_template` SET `spell1` = 15580, `spell2` = 15655, `spell3` = 22572, `spell4` = 20691 WHERE `entry` = 14321; +UPDATE `creature_template` SET `spell1` = 15580, `spell2` = 15655, `spell3` = 20691 WHERE `entry` = 14326; +UPDATE `creature_template` SET `spell1` = 15580, `spell2` = 17307, `spell3` = 20691 WHERE `entry` = 14323; +UPDATE `creature_template` SET `spell1` = 22419, `spell2` = 22420, `spell3` = 22421 WHERE `entry` = 13280; +UPDATE `creature_template` SET `spell1` = 22422 WHERE `entry` = 14122; +UPDATE `creature_template` SET `spell1` = 5116, `spell2` = 20904, `spell3` = 14290, `spell4` = 14295 WHERE `entry` = 11488; +UPDATE `creature_template` SET `spell1` = 16128, `spell2` = 15550, `spell3` = 22899 WHERE `entry` = 11496; +UPDATE `creature_template` SET `spell1` = 22909 WHERE `entry` = 14396; +UPDATE `creature_template` SET `spell1` = 15708, `spell2` = 24375 WHERE `entry` = 11501; +UPDATE `creature_template` SET `spell1` = 11668, `spell2` = 14887 WHERE `entry` = 14327; +UPDATE `creature_template` SET `spell1` = 10984 WHERE `entry` = 14506; +UPDATE `creature_template` SET `spell1` = 10894, `spell2` = 10947, `spell3` = 18807 WHERE `entry` = 11487; +UPDATE `creature_template` SET `spell1` = 20691, `spell2` = 22920 WHERE `entry` = 11486; +UPDATE `creature_template` SET `spell1` = 22424, `spell2` = 10151, `spell3` = 16144 WHERE `entry` = 14354; +UPDATE `creature_template` SET `spell1` = 20691, `spell2` = 24375 WHERE `entry` = 11498; +UPDATE `creature_template` SET `spell1` = 15550, `spell2` = 22924, `spell3` = 22994 WHERE `entry` = 11489; +UPDATE `creature_template` SET `spell1` = 22478, `spell2` = 22651 WHERE `entry` = 11490; +UPDATE `creature_template` SET `spell1` = 20832, `spell2` = 16102, `spell3` = 15530, `spell4` = 16170 WHERE `entry` = 11444; +UPDATE `creature_template` SET `spell1` = 22572, `spell2` = 22916 WHERE `entry` = 11450; +UPDATE `creature_template` SET `spell1` = 13737, `spell2` = 20677, `spell3` = 24317 WHERE `entry` = 11441; +UPDATE `creature_template` SET `spell1` = 19474 WHERE `entry` = 11457; +UPDATE `creature_template` SET `spell1` = 22460, `spell2` = 22272 WHERE `entry` = 13197; +UPDATE `creature_template` SET `spell1`=512, `spell2`=1010, `spell3`=0, `spell4`=0 WHERE `entry`=10415; +UPDATE `creature_template` SET `spell1`=1096, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10917; +UPDATE `creature_template` SET `spell1`=20603, `spell2`=15398, `spell3`=9256, `spell4`=20741 WHERE `entry`=10813; +UPDATE `creature_template` SET `spell1`=16565, `spell2`=16867, `spell3`=18327, `spell4`=0 WHERE `entry`=10436; +UPDATE `creature_template` SET `spell1`=16866, `spell2`=1604, `spell3`=0, `spell4`=0 WHERE `entry`=10697; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=16866, `spell3`=0, `spell4`=0 WHERE `entry`=10416; +UPDATE `creature_template` SET `spell1`=17439, `spell2`=15584, `spell3`=1604, `spell4`=0 WHERE `entry`=10394; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=5884, `spell3`=0, `spell4`=0 WHERE `entry`=11121; +UPDATE `creature_template` SET `spell1`=16140, `spell2`=1604, `spell3`=0, `spell4`=0 WHERE `entry`=10383; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=3589, `spell3`=0, `spell4`=0 WHERE `entry`=8530; +UPDATE `creature_template` SET `spell1`=23382, `spell2`=15615, `spell3`=16496, `spell4`=0 WHERE `entry`=10997; +UPDATE `creature_template` SET `spell1`=13020, `spell2`=23413, `spell3`=0, `spell4`=0 WHERE `entry`=10425; +UPDATE `creature_template` SET `spell1`=17293, `spell2`=23462, `spell3`=17274, `spell4`=0 WHERE `entry`=10811; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=15749, `spell3`=11972, `spell4`=13534 WHERE `entry`=13118; +UPDATE `creature_template` SET `spell1`=11831, `spell2`=23412, `spell3`=1604, `spell4`=0 WHERE `entry`=10419; +UPDATE `creature_template` SET `spell1`=5101, `spell2`=17143, `spell3`=13005, `spell4`=0 WHERE `entry`=12337; +UPDATE `creature_template` SET `spell1`=13005, `spell2`=1604, `spell3`=20005, `spell4`=0 WHERE `entry`=10421; +UPDATE `creature_template` SET `spell1`=36647, `spell2`=17143, `spell3`=1604, `spell4`=0 WHERE `entry`=10424; +UPDATE `creature_template` SET `spell1`=15749, `spell2`=33871, `spell3`=1604, `spell4`=0 WHERE `entry`=10418; +UPDATE `creature_template` SET `spell1`=17445, `spell2`=1604, `spell3`=39077, `spell4`=0 WHERE `entry`=11120; +UPDATE `creature_template` SET `spell1`=17287, `spell2`=1604, `spell3`=22645, `spell4`=0 WHERE `entry`=10420; +UPDATE `creature_template` SET `spell1`=22645, `spell2`=17165, `spell3`=22947, `spell4`=0 WHERE `entry`=10426; +UPDATE `creature_template` SET `spell1`=36033, `spell2`=17145, `spell3`=1604, `spell4`=0 WHERE `entry`=11043; +UPDATE `creature_template` SET `spell1`=36176, `spell2`=36947, `spell3`=1604, `spell4`=0 WHERE `entry`=10423; +UPDATE `creature_template` SET `spell1`=38094, `spell2`=1604, `spell3`=6788, `spell4`=0 WHERE `entry`=11054; +UPDATE `creature_template` SET `spell1`=15451, `spell2`=16927, `spell3`=16144, `spell4`=15534 WHERE `entry`=10422; +UPDATE `creature_template` SET `spell1`=22412, `spell2`=4962, `spell3`=1604, `spell4`=3589 WHERE `entry`=10413; +UPDATE `creature_template` SET `spell1`=15471, `spell2`=1604, `spell3`=3589, `spell4`=16430 WHERE `entry`=10412; +UPDATE `creature_template` SET `spell1`=16401, `spell2`=12023, `spell3`=1604, `spell4`=20812 WHERE `entry`=8556; +UPDATE `creature_template` SET `spell1`=4629, `spell2`=20549, `spell3`=20812, `spell4`=21949 WHERE `entry`=11859; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=15530, `spell3`=15531, `spell4`=16458 WHERE `entry`=15607; +UPDATE `creature_template` SET `spell1`=16388, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10557; +UPDATE `creature_template` SET `spell1`=18200, `spell2`=1604, `spell3`=9791, `spell4`=16458 WHERE `entry`=10407; +UPDATE `creature_template` SET `spell1`=7964, `spell2`=20712, `spell3`=1604, `spell4`=23511 WHERE `entry`=11058; +UPDATE `creature_template` SET `spell1`=16249, `spell2`=16143, `spell3`=16449, `spell4`=0 WHERE `entry`=11136; +UPDATE `creature_template` SET `spell1`=22687, `spell2`=1604, `spell3`=0, `spell4`=0 WHERE `entry`=10385; +UPDATE `creature_template` SET `spell1`=12538, `spell2`=15608, `spell3`=16172, `spell4`=1604 WHERE `entry`=10406; +UPDATE `creature_template` SET `spell1`=17286, `spell2`=1604, `spell3`=20830, `spell4`=0 WHERE `entry`=10812; +UPDATE `creature_template` SET `spell1`=3589, `spell2`=16867, `spell3`=1604, `spell4`=0 WHERE `entry`=8541; +UPDATE `creature_template` SET `spell1`=17685, `spell2`=18663, `spell3`=19643, `spell4`=20812 WHERE `entry`=16101; +UPDATE `creature_template` SET `spell1`=16793, `spell2`=10887, `spell3`=14099, `spell4`=1604 WHERE `entry`=10435; +UPDATE `creature_template` SET `spell1`=15850, `spell2`=16249, `spell3`=20743, `spell4`=16869 WHERE `entry`=10438; +UPDATE `creature_template` SET `spell1`=12734, `spell2`=16172, `spell3`=1604, `spell4`=6788 WHERE `entry`=11032; +UPDATE `creature_template` SET `spell1`=16143, `spell2`=1604, `spell3`=15043, `spell4`=0 WHERE `entry`=10382; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=6788, `spell3`=0, `spell4`=0 WHERE `entry`=11197; +UPDATE `creature_template` SET `spell1`=1604, `spell2`=6788, `spell3`=0, `spell4`=0 WHERE `entry`=11030; +UPDATE `creature_template` SET `spell1`=5568, `spell2`=17307, `spell3`=1604, `spell4`=6788 WHERE `entry`=10439; +UPDATE `creature_template` SET `spell1`=19813, `spell2`=4283, `spell3`=0, `spell4`=0 WHERE `entry`=11658; +UPDATE `creature_template` SET `spell1`=19393, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=11668; +UPDATE `creature_template` SET `spell1`=25787, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=12101; +UPDATE `creature_template` SET `spell1`=19364, `spell2`=19365, `spell3`=19369, `spell4`=19367 WHERE `entry`=11673; +UPDATE `creature_template` SET `spell1`=4283, `spell2`=19813, `spell3`=0, `spell4`=0 WHERE `entry`=11659; +UPDATE `creature_template` SET `spell1`=20228, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=12143; +UPDATE `creature_template` SET `spell1`=20203, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=11669; +UPDATE `creature_template` SET `spell1`=24668, `spell2`=23952, `spell3`=10960, `spell4`=0 WHERE `entry`=11663; +UPDATE `creature_template` SET `spell1`=23952, `spell2`=20294, `spell3`=0, `spell4`=0 WHERE `entry`=11662; +UPDATE `creature_template` SET `spell1`=20677, `spell2`=20740, `spell3`=0, `spell4`=0 WHERE `entry`=12119; +UPDATE `creature_template` SET `spell1`=20623, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=11664; +UPDATE `creature_template` SET `spell1`=19778, `spell2`=19771, `spell3`=0, `spell4`=0 WHERE `entry`=11671; +UPDATE `creature_template` SET `spell1`=21081, `spell2`=19730, `spell3`=0, `spell4`=0 WHERE `entry`=11661; +UPDATE `creature_template` SET `spell1`=19626, `spell2`=19630, `spell3`=19631, `spell4`=0 WHERE `entry`=11667; +UPDATE `creature_template` SET `spell1`=19798, `spell2`=19496, `spell3`=0, `spell4`=0 WHERE `entry`=12806; +UPDATE `creature_template` SET `spell1`=19635, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=11666; +UPDATE `creature_template` SET `spell1`=19642, `spell2`=19644, `spell3`=0, `spell4`=0 WHERE `entry`=12100; +UPDATE `creature_template` SET `spell1`=19641, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=12076; +UPDATE `creature_template` SET `spell1`=20294, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=12099; +UPDATE `creature_template` SET `spell1`=18108, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=12265; +UPDATE `creature_template` SET `spell1`=22689, `spell2`=19319, `spell3`=0, `spell4`=0 WHERE `entry`=11672; +UPDATE `creature_template` SET `spell1`=35178, `spell2`=15749, `spell3`=0, `spell4`=0 WHERE `entry`=10509; +UPDATE `creature_template` SET `spell1`=35178, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=9819; +UPDATE `creature_template` SET `spell1`=30688, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10319; +UPDATE `creature_template` SET `spell1`=20816, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=9817; +UPDATE `creature_template` SET `spell1`=32192, `spell2`=18108, `spell3`=0, `spell4`=0 WHERE `entry`=9818; +UPDATE `creature_template` SET `spell1`=23462, `spell2`=23341, `spell3`=17274, `spell4`=0 WHERE `entry`=9816; +UPDATE `creature_template` SET `spell1`=24317, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10258; +UPDATE `creature_template` SET `spell1`=145, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10161; +UPDATE `creature_template` SET `spell1`=0, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10683; +UPDATE `creature_template` SET `spell1`=28725, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10264; +UPDATE `creature_template` SET `spell1`=12461, `spell2`=22591, `spell3`=16172, `spell4`=24317 WHERE `entry`=10899; +UPDATE `creature_template` SET `spell1`=12461, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10317; +UPDATE `creature_template` SET `spell1`=0, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10762; +UPDATE `creature_template` SET `spell1`=0, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10742; +UPDATE `creature_template` SET `spell1`=30014, `spell2`=32370, `spell3`=0, `spell4`=0 WHERE `entry`=10447; +UPDATE `creature_template` SET `spell1`=22414, `spell2`=23512, `spell3`=0, `spell4`=0 WHERE `entry`=10442; +UPDATE `creature_template` SET `spell1`=20691, `spell2`=15589, `spell3`=23931, `spell4`=0 WHERE `entry`=10429; +UPDATE `creature_template` SET `spell1`=20667, `spell2`=20712, `spell3`=18763, `spell4`=0 WHERE `entry`=10339; +UPDATE `creature_template` SET `spell1`=11286, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10318; +UPDATE `creature_template` SET `spell1`=23462, `spell2`=25668, `spell3`=0, `spell4`=0 WHERE `entry`=10372; +UPDATE `creature_template` SET `spell1`=28168, `spell2`=27580, `spell3`=0, `spell4`=0 WHERE `entry`=10371; +UPDATE `creature_template` SET `spell1`=23462, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10083; +UPDATE `creature_template` SET `spell1`=20741, `spell2`=24668, `spell3`=0, `spell4`=0 WHERE `entry`=10162; +UPDATE `creature_template` SET `spell1`=17883, `spell2`=16785, `spell3`=0, `spell4`=0 WHERE `entry`=10430; +UPDATE `creature_template` SET `spell1`=23462, `spell2`=20691, `spell3`=23023, `spell4`=23931 WHERE `entry`=10363; +UPDATE `creature_template` SET `spell1`=20276, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10814; +UPDATE `creature_template` SET `spell1`=13496, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=11598; +UPDATE `creature_template` SET `spell1`=27559, `spell2`=20691, `spell3`=12020, `spell4`=30138 WHERE `entry`=10487; +UPDATE `creature_template` SET `spell1`=17715, `spell2`=27559, `spell3`=20276, `spell4`=0 WHERE `entry`=10491; +UPDATE `creature_template` SET `spell1`=11660, `spell2`=18807, `spell3`=0, `spell4`=0 WHERE `entry`=10470; +UPDATE `creature_template` SET `spell1`=18270, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10481; +UPDATE `creature_template` SET `spell1`=18116, `spell2`=17695, `spell3`=20741, `spell4`=15584 WHERE `entry`=10433; +UPDATE `creature_template` SET `spell1`=17472, `spell2`=15584, `spell3`=0, `spell4`=0 WHERE `entry`=10482; +UPDATE `creature_template` SET `spell1`=18106, `spell2`=18116, `spell3`=15584, `spell4`=0 WHERE `entry`=11261; +UPDATE `creature_template` SET `spell1`=18116, `spell2`=20741, `spell3`=18144, `spell4`=0 WHERE `entry`=10506; +UPDATE `creature_template` SET `spell1`=11672, `spell2`=12020, `spell3`=18116, `spell4`=0 WHERE `entry`=10505; +UPDATE `creature_template` SET `spell1`=16359, `spell2`=3584, `spell3`=18116, `spell4`=0 WHERE `entry`=10901; +UPDATE `creature_template` SET `spell1`=24375, `spell2`=18813, `spell3`=18116, `spell4`=0 WHERE `entry`=11622; +UPDATE `creature_template` SET `spell1`=15550, `spell2`=20691, `spell3`=18116, `spell4`=0 WHERE `entry`=10507; +UPDATE `creature_template` SET `spell1`=11668, `spell2`=11700, `spell3`=18116, `spell4`=0 WHERE `entry`=10504; +UPDATE `creature_template` SET `spell1`=24673, `spell2`=18270, `spell3`=18116, `spell4`=0 WHERE `entry`=10503; +UPDATE `creature_template` SET `spell1`=15584, `spell2`=17472, `spell3`=0, `spell4`=0 WHERE `entry`=11439; +UPDATE `creature_template` SET `spell1`=18116, `spell2`=19460, `spell3`=6215, `spell4`=15487 WHERE `entry`=10502; +UPDATE `creature_template` SET `spell1`=18702, `spell2`=10212, `spell3`=18116, `spell4`=0 WHERE `entry`=1853; +UPDATE `creature_template` SET `spell1`=16350, `spell2`=21369, `spell3`=8398, `spell4`=18033 WHERE `entry`=10508; +UPDATE `creature_template` SET `spell1`=27224, `spell2`=30900, `spell3`=3609, `spell4`=0 WHERE `entry`=14861; +UPDATE `creature_template` SET `spell1`=18278, `spell2`=29405, `spell3`=12020, `spell4`=0 WHERE `entry`=10498; +UPDATE `creature_template` SET `spell1`=24063, `spell2`=18270, `spell3`=0, `spell4`=0 WHERE `entry`=10495; +UPDATE `creature_template` SET `spell1`=12020, `spell2`=18278, `spell3`=25304, `spell4`=0 WHERE `entry`=11263; +UPDATE `creature_template` SET `spell1`=16469, `spell2`=25349, `spell3`=0, `spell4`=0 WHERE `entry`=11551; +UPDATE `creature_template` SET `spell1`=25304, `spell2`=11660, `spell3`=12020, `spell4`=0 WHERE `entry`=10477; +UPDATE `creature_template` SET `spell1`=25304, `spell2`=12020, `spell3`=10161, `spell4`=0 WHERE `entry`=10472; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10475; +UPDATE `creature_template` SET `spell1`=25304, `spell2`=10161, `spell3`=10473, `spell4`=0 WHERE `entry`=10469; +UPDATE `creature_template` SET `spell1`=11660, `spell2`=15571, `spell3`=10161, `spell4`=0 WHERE `entry`=11284; +UPDATE `creature_template` SET `spell1`=17689, `spell2`=15571, `spell3`=18270, `spell4`=0 WHERE `entry`=10480; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=30138, `spell3`=0, `spell4`=0 WHERE `entry`=10497; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=30138, `spell3`=0, `spell4`=0 WHERE `entry`=10479; +UPDATE `creature_template` SET `spell1`=17504, `spell2`=12867, `spell3`=0, `spell4`=0 WHERE `entry`=14513; +UPDATE `creature_template` SET `spell1`=17504, `spell2`=12867, `spell3`=24673, `spell4`=0 WHERE `entry`=14520; +UPDATE `creature_template` SET `spell1`=30138, `spell2`=11660, `spell3`=0, `spell4`=0 WHERE `entry`=14521; +UPDATE `creature_template` SET `spell1`=11556, `spell2`=8140, `spell3`=23262, `spell4`=0 WHERE `entry`=14518; +UPDATE `creature_template` SET `spell1`=10894, `spell2`=23244, `spell3`=0, `spell4`=0 WHERE `entry`=14519; +UPDATE `creature_template` SET `spell1`=11556, `spell2`=8140, `spell3`=15571, `spell4`=0 WHERE `entry`=14514; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=23244, `spell3`=0, `spell4`=0 WHERE `entry`=14512; +UPDATE `creature_template` SET `spell1`=12292, `spell2`=15571, `spell3`=28168, `spell4`=0 WHERE `entry`=10488; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=24063, `spell3`=18270, `spell4`=12021 WHERE `entry`=10485; +UPDATE `creature_template` SET `spell1`=10161, `spell2`=13021, `spell3`=20883, `spell4`=0 WHERE `entry`=11257; +UPDATE `creature_template` SET `spell1`=15571, `spell2`=0, `spell3`=0, `spell4`=0 WHERE `entry`=10478; +UPDATE `creature_template` SET `spell1`=17504, `spell2`=26554, `spell3`=35011, `spell4`=0 WHERE `entry`=10486; +UPDATE `creature_template` SET `spell1`=19627, `spell2`=18816, `spell3`=13021, `spell4`=0 WHERE `entry`=10432; +UPDATE `creature_template` SET `spell1`=18807, `spell2`=10876, `spell3`=0, `spell4`=0 WHERE `entry`=10471; +UPDATE `creature_template` SET `spell1`=10876, `spell2`=25304, `spell3`=18647, `spell4`=0 WHERE `entry`=10500; +UPDATE `creature_template` SET `spell1`=29684, `spell2`=25051, `spell3`=0, `spell4`=0 WHERE `entry`=10489; + +UPDATE `creature_template` SET `spell1` = 24317, `spell2` = 19644 WHERE `entry` = 8890; +UPDATE `creature_template` SET `spell1` = 30901, `spell2` = 27581, `spell3` = 34510 WHERE `entry` = 8891; +UPDATE `creature_template` SET `spell1` = 29560, `spell2` = 19644 WHERE `entry` = 8892; +UPDATE `creature_template` SET `spell1` = 40082, `spell2` = 30901 WHERE `entry` = 8893; +UPDATE `creature_template` SET `spell1` = 8812, `spell2` = 10961, `spell3` = 10947 WHERE `entry` = 8894; +UPDATE `creature_template` SET `spell1` = 25292, `spell2` = 1020 WHERE `entry` = 8895; +UPDATE `creature_template` SET `spell1` = 17143, `spell2` = 10293, `spell3` = 25292 WHERE `entry` = 8898; diff --git a/src/bindings/scripts/sql/optional/mangos_optional_generic_creature.sql b/src/bindings/scripts/sql/optional/mangos_optional_generic_creature.sql new file mode 100644 index 00000000000..a787b584e66 --- /dev/null +++ b/src/bindings/scripts/sql/optional/mangos_optional_generic_creature.sql @@ -0,0 +1,283 @@ +/* */ + +/* WORLD CREATURE These are creatures to be found in more than one specific zone */ +/* Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (14881); + +/* */ +/* ZONE */ +/* */ + +/* ALTERAC MOUNTAINS */ +/* Crushridge Mage, Argus Shadow Mage, Dalaran Summoner, Archmage Ansirem Runeweaver */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (2255, 2318, 2358, 2543); + +/* ALTERAC VALLEY */ +/* Wildpaw Mystic */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (11838); + +/* ARATHI HIGHLANDS */ +/* Feeboz, Boulderfist Shaman, Boulderfist Magus, Syndicate Magus, Dark Iron Shadowcaster, Witherbark Axe Thrower, Plains Creeper, Giant Plains Creeper */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4063, 2570, 2567, 2591, 2577, 2554, 2563, 2565); + +/* ASHENVALE */ +/* Shadethicket Raincaller, Forsaken Seeker, Dark Strand Cultist, Dark Strand Adept */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (3783, 3732, 3725, 3728); + + +/* */ +/* AUCHINDOUN */ +/* */ + + + +/* AZSHARA */ +/* Draconic Magelord, Draconic Mageweaver, Archmage Xylem */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (6129, 6131, 8379); + +/* BADLANDS */ +/* Dustbelcher Ogre Mage, Dustbelcher Mystic, Shadowmage Vivian Lagrave */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (2720, 2907, 9078); + +/* BARRENS */ +/* Kolkar Stormer, Razormane Geomancer, Razormane Seer, Razormane Mystic, Razormane Hunter, Razormane Warfrenzy, Razormane Water Seeker, Silithid Creeper, Elder Mystic Razorsnout, Horde Axe Thrower */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (3273, 3269, 3458, 3271, 3265, 3459, 3267, 3250, 3270, 9458); + +/* BLACKFATHOM DEPTHS */ +/* Blindlight Oracle, Blindlight Muckdweller, Blindlight Murloc, Twilight Shadowmage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4820, 4819, 4818, 4813); + + +/* Hedrum the Creeper, Dark Keeper Zimrel */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (9032, 9441); +/* Anvilrage military */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (8890, 8891, 8892, 8893, 8894, 8895, 8898); +/* Cave Creeper */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (8933); + +/* BLACKROCK SPIRE */ +/* Blackhand Dreadweaver,Blackhand Summoner,Blackhand Veteran,Blackhand Elite,Blackhand Assassin,Blackhand Iron Guard,Chromatic Whelp,Chromatic Dragonspawn,Rookery Whelp,Rookery Guardian,Rage Talon Captain,Rage Talon Fire Tongue,Rage Talon Flamescale */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (9817, 9818, 9819, 10317, 10318, 10319, 10442, 10447, 10161, 10258, 10371, 10372, 10083); +/* Spirestone Battle Mage, Spirestone Mystic, Smolderthorn Mystic, Smolderthorn Axe Thrower, Bloodaxe Summoner , Spire Spider, Spire Spiderling */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (9197, 9198, 9239, 9267, 9717, 10374, 10375); + + +/* BLACKROCK SPIRE Upper */ +/* Jed Runewatcher,Solakar Flamewreath,Goraluk Anvilcrack */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry`IN (10509,10264,10899); + +/* BLACKWING LAIR */ +/* Trash Mobs */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (12420); + + +/* BLASTED LANDS */ +/* Dreadmaul Ogre Mage, Bloodmage Drazial, Bloodmage Lynnore, Archmage Allistarj */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (5975, 7505, 7506, 7666); + +/* BURNING STEPPES */ +/* Blackrock Sorcerer, Blackrock Warlock, Scalding Broodling, Thaurissan Agent, Firegut Ogre Mage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7026, 7028, 7048, 7038, 7034); + + +/* DARKSHORE */ +/* Delmanis the Hated, Greymist Oracle, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (3662, 2207); + +/* DEADMINES */ +/* Defias Squallshaper, Defias Magician, Defias Conjurer, Defias Overseer, Defias Watchman, Defias Wizard, Defias Evoker, Defias Pirate, Defias Taskmaster, Defias Miner, Defias Strip Miner, Defias Henchman, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1732, 1726, 619, 634, 1725, 4418, 1729, 657, 4417, 598, 4416, 594); + +/* DEADWIND PASS */ +/* Deadwind Ogre Mage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7379); + +/* DESOLACE */ +/* Burning Blade Shadowmage, Burning Blade Summoner, Mage Hunter */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4667, 4668, 4681); + +/* DIRE MAUL */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (11492, 14325, 14324, 14321, 14326, 14323, 13280, 14122, 11488, 11496, 14396, 11501, 14327, 14506, 11487, 11486, 14354, 11498, 11489, 11490, 11444, 11450, 11441, 11457, 13197); +/* Gordok Mage-Lord, Highborne Summoner */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (11444, 11466); + +/* DUN MOROGH */ +/* Frostmane Novice, Frostmane Seer, Rockjaw Skullthumper, Rockjaw Bonesnapper, Rockjaw Ambusher, Rockjaw Backbreaker, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (946, 1397, 1115, 1117, 1116, 1118); + +/* DUROTAR */ +/* Razormane Scout, Razormane Dustrunner, Razormane Quilboar, Razormane Battleguard, Vile Familiar */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (3112, 3113, 3111, 3114, 3101); + +/* DUSKWOOD */ +/* Eliza, Nightbane Shadow Weaver, Skeletal Mage, Defias Night Runner, Defias Night Blade, Skeletal Fiend, Skeletal Healer, Skeletal Mage, Skeletal Raider, Skeletal Warder, Skeletal Warrior, Venom Web Spider, Pygmy Venom Web Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (314, 533, 203, 215, 909, 531, 787, 203, 1110, 785, 48, 217, 539); + +/* DUSTWALLOW MARSH */ +/* Mirefin Murloc, Darkmist Spider, Withervine Creeper, Darkfang Creeper, Darkfang Spider, Giant Darkfang Spider, Archmage Tervosh */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4359, 4376, 4382, 4412, 4413, 4415, 4967); + +/* EASTERN PLAGUELANDS */ +/* Crimson Bodyguard, Crimson Courier, Crypt Walker, Hate Shrieker, Cursed Mage, Shadowmage, Dark Summoner, Scarlet Archmage, Archmage Angela Dosantos */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (13118, 12337, 8556, 8541, 8524, 8550, 8551, 9451, 16116); + +/* ELWYNN FOREST */ +/* Hogger, Defias Rogue Wizard, Defias Enchanter, Defias Bodyguard, Defias Bandit, Defias Cutpurse, Murloc Lurker, Murloc Forager, Forest Spider, Mine Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (448, 474, 910, 6866, 116, 94, 732, 46, 30, 43); + +/* FELWOOD */ +/* Timbermaw Mystic */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (11552); + +/* FERALAS */ +/* Gordunni Ogre Mage, Gordunni Mage-Lord, Woodpaw Mystic, Gordok Ogre-Mage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (5237, 5239, 5254, 11443); + +/* GNOMEREGAN */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (6206, 6208, 6209, 6215, 6219, 6229, 6228, 6235, 7361, 6407, 6220, 6218, 7603, 6223, 6222, 6234, 6233, 6226, 6227, 6225, 6230, 7079); + +/* */ +/* HELLFIRE CITADEL */ +/* */ + +/* Laughing Skull Legionnaire, Laughing Skull Warden, Laughing Skull Rogue, Hellfire Imp, Shadowmoon Channeler, Fel Orc Neophyte, Shadowmoon Technician, Shadowmoon Adept, Hellfire Familiar, Felguard Brute, Shadowmoon Summoner, Shadowmoon Warlock */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (17626, 17624, 17491, 17477, 17653, 17429, 17414, 17397, 19016, 18894, 17395, 17371); + +/* HELLFIRE RAMPARTS */ +/* Vazruden, Omor the Unscarred, Watchkeeper Gargolmar */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (17537, 17308, 17306); +/* Hellfire Watcher , Bonechewer Hungerer, Bonechewer Beastmaster, Bonechewer Ravener, Bonechewer Ripper, Bonechewer Destroyer, Bleeding Hollow Archer, Bleeding Hollow Darkcaster, Hellfire Sentry , Bleeding Hollow Scryer */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (17309, 17259, 17455, 17264, 17281, 17271, 17270, 17269, 17517, 17478); + +/* SHATTERED HALLS */ +/* Warchief Kargath Bladefist */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (16808); +/* Shattered Hand Centurion, Shadowmoon Darkcaster, Shattered Hand Champion, Sharpshooter Guard, Shattered Hand Assassin, Warbringer O'mrogg, Grand Warlock Nethekurse, Shattered Hand Sharpshooter, Shattered Hand Legionnaire, Shattered Hand Reaver, Shattered Hand Brawler */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (17465, 17694, 17671, 17622, 17695, 16809, 16807, 16704, 16700, 16699, 16593); + + +/* HILLSBRAD FOOTHILLS */ +/* Syndicate Shadow Mage, Elder Moss Creeper, Giant Moss Creeper(also AM), Forest Moss Creeper, Writhing Mage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (2244, 2348, 2349, 2350, 7075); + +/* HINTERLANDS */ +/* Vilebranch Axe Thrower, Mystic Yayo'jin */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (2639, 14739); + +/* LOCH MODAN */ +/* Mo'grosh Mystic, Tunnel Rat Geomancer, Magosh , Stonesplinter Scout, Stonesplinter Seer, Stonesplinter Geomancer, Stonesplinter Skullthumper, Stonesplinter Bonesnapper, Stonesplinter Shaman, Stonesplinter Digger, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1183, 1174, 1399, 1162, 1166, 1165, 1163, 1164, 1197, 1167); + +/* MULGORE */ +/* Bristleback Shaman, Bristleback Interloper, Bristleback Quilboar, Bristleback Battleboar, Bristleback Interloper, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (2953, 3232, 2952, 2954, 3232); + +/* NAXXRAMAS */ +/* Dread Creeper, Archmage Tarsis Kir-Moldir */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (15974, 16381); + +/* ONYXIA'S LAIR */ +/* Onyxian Whelp */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (11262); + +/* RAZORFEN DOWNS */ +/* Mordresh Fire Eye, Plaguemaw the Rotting, Tuten\'kash, Ragglesnout, Glutton */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7357, 7356, 7355, 7354, 8567); +/* Skeletal Frostweaver, Skeletal Shadowcaster, Skeletal Summoner, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7341, 7340, 7342); + +/* REDRIDGE MOUNTAINS */ +/* Blackrock Shadowcaster, Murloc Minor Tidecaller, Murloc Nightcrawler, Murloc Tidecaller, Murloc Scout, Murloc Shorestriker, Shadowhide Darkweaver, Rabid Shadowhide Gnoll, Shadowhide Gnoll, Shadowhide Brute, Shadowhide Warrior, Shadowhide Assassin, Redridge Mystic, Redridge Poacher, Redridge Thrasher, Redridge Basher, Black Dragon Whelp, Tarantula, Greater Tarantula, Blackrock Summoner */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (436, 548, 544, 545, 578, 1083, 422, 429, 434, 433, 432, 568, 579, 430, 424, 712, 446, 441, 442, 505, 4463); + +/* SCARLET MONASTERY */ +/* Fallen Champion, Scarlet Chaplain, Scarlet Adept, Scarlet Wizard, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (6488, 4299, 4296, 4300); + +/* SCHOLOMANCE */ +/* Kirtonos the Herald, Rattlegore, Marduk Blackpool, Risen Guard, Risen Bonewarder, Risen Lackey, Risen Aberration, Risen Warrior, Risen Protector, Risen Construct, Risen Guardian, Diseased Ghoul, Ragged Ghoul, Spectral Projection, Spectral Teacher, Necrofiend, Scholomance Neophyte, Scholomance Acolyte, Scholomance Occultist */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (10506, 11622, 10433, 10489, 10491, 10482, 10485, 10486, 10487, 10488, 11598, 10495, 10497, 11263, 10500, 11551, 10470, 10471, 10472); +/* Scholomance Student, Scholomance Necromancer, Scholomance Adept, Scholomance Handler, Splintered Skeleton, Skulking Corpse, Unstable Corpse, Reanimated Corpse, Aspect of Banality, Aspect of Corruption, Aspect of Malice, Aspect of Shadow, Blood Steward of Kirtonos, Spectral Tutor, Dark Shade, Corrupted Spirit, Malicious Spirit, Banal Spirit, Scholomance Dark Summoner */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (10475, 10477, 10469, 11257, 10478, 10479, 10480, 10481, 14518, 14519, 14520, 14521, 14861, 10498, 11284, 14512, 14513, 14514, 11582); + +/* SEARING GORGE */ +/* Glassweb Spider, Searing Lava Spider, Greater Lava Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (5856, 5857, 5858); + +/* SHADOWFANG KEEP */ +/* Archmage Arugal */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4275); + + +/* SILITHUS */ +/* Orgrimmar Legion Axe Thrower, Stormwind Archmage */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (15617, 15859); + +/* SILVERPINE FOREST */ +/* Dalaran Apprentice, Dalaran Conjuror, Dalaran Mage, Dalaran Wizard, Rot Hide Mystic, Rot Hide Gladerunner, Rot Hide Brute, Rot Hide Plague Weaver, Rot Hide Savage, Rot Hide Bruiser, Mist Creeper, Dalaran Apprentice, Dalaran Wizard, Dalaran Protector, Dalaran Warder, Dalaran Mage, Dalaran Conjuror, Lake Creeper, Elder Lake Creeper, Vile Fin Shorecreeper, Nightlash, Archmage Ataeric */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1867, 1915, 1914, 1889, 1773, 1772, 1939, 1940, 1942, 1944, 1781, 1867, 1889, 1912, 1913, 1914, 1915, 1955, 1956, 1957, 1983, 2120); + +/* STOCKADES */ +/* Defias Captive, Defias Convict, Defias Inmate, Defias Insurgent, Defias Prisoner, */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1707, 1711, 1708, 1715, 1706); + +/* STONETALON MOUNTAINS */ +/* Deepmoss Creeper */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (4005); + +/* STRANGLETHORN VALE */ +/* Bloodscalp Shaman, Bloodsail Elder Magus, Bloodsail Mage, Bloodsail Warlock, Bloodscalp Axe Thrower, Skullsplitter Axe Thrower, Bloodscalp Mystic, Skullsplitter Mystic */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (697, 1653, 1562, 1564, 694, 696, 701, 780); + +/* STRATHOLME */ +/* Jarien, Aurius, Ash'ari Crystal, Elder Farwhisper, Mindless Undead */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (16101, 10917, 10415, 15607, 11030); + +/* SUNKEN TEMPLE */ +/* Zekkis, Kazkaz the Unholy, Spawn of Hakkar, Shade of Eranikus, Jammal'an the Prophet, Ogom the Wretched, Zolo, Gasher, Loro, Hukku, Zul'Lor, Mijan, Morphaz, Weaver, Dreamscythe, Hazzas, Avatar of Hakkar, Atal'alarion */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (5400, 5401, 5708, 5709, 5710, 5711, 5712, 5713, 5714, 5715, 5716, 5717, 5719, 5720, 5721, 5722, 8443, 8580); +/* Jade, Murk Slitherer, Murk Spitter, Murk Worm, Saturated Ooze, Fungal Ooze, Cursed Atal'ai, Atal'ai Witch Doctor, Enthralled Atal'ai, Mummified Atal'ai, Unliving Atal'ai, Atal'ai Priest, Atal'ai Corpse Eater, Atal'ai Deathwalker, Atal'ai High Priest, Nightmare Scalebane, Nightmare Wyrmkin, Nightmare Wanderer, Hakkari Frostwing, Nightmare Whelp, Atal'ai Skeleton, Hakkari Sapper, Hakkari Minion, Hakkari Bloodkeeper, Nightmare Suppressor */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1063, 5224, 5225, 5226, 5228, 5235, 5243, 5259, 5261, 5263, 5267, 5269, 5270, 5271, 5273, 5277, 5280, 5283, 5291, 8319, 8324, 8336, 8437, 8438, 8497); + +/* SWAMP OF SORROWS */ +/* Marsh Murloc, Adolescent Whelp, Dreaming Whelp, Wyrmkin Dreamwalker, Scalebane Captain, Deathstrike Tarantula */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (747, 740, 741, 743, 745, 769); + +/* TANARIS */ +/* Dunemaul Ogre Mage, Wastewander Shadow Mage, Sandfury Axe Thrower */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (5473, 5617, 5646); + +/* TELDRASSIL */ +/* Gnarlpine Mystic, Gnarlpine Pathfinder, Bloodfeather Sorceress, Webwood Spider, Giant Webwood Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7235, 2012, 2018, 1986, 2001); + +/* TIRISFAL GLADES */ +/* Scarlet Neophyte, Rot Hide Gnoll, Young Night Web Spider, Night Web Spider, Vicious Night Web Spider */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1539, 1674, 1504, 1505, 1555); + +/* UN'GORO CRATER */ +/* Tar Creeper */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (6527); + +/* WAILING CAVERNS */ +/* Deviate Creeper */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (3632); + +/* WESTERN PLAGUELANDS */ +/* Skeletal Acolyte, Skeletal Flayer, Skeletal Sorcerer, Scarlet Mage, Araj the Summoner */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1789, 1783, 1784, 1826, 1852); + +/* WESTFALL */ +/* Defias Pillager, Defias Smuggler, Defias Trapper, Defias Highwayman, Defias Knuckleduster, Defias Pathstalker, Defias Looter, Murloc Oracle, Murloc Hunter, Murloc Warrior, Murloc Tidehunter, Murloc Coastrunner, Riverpaw Scout, Riverpaw Mongrel, Riverpaw Brute, Riverpaw Herbalist, Defias Renegade Mage, Riverpaw Mystic */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (589, 95, 504, 122, 449, 121, 590, 517, 458, 171, 127, 126, 500, 123, 124, 501, 450, 453); + +/* WETLANDS */ +/* Bluegill Forager, Bluegill Murloc, Bluegill Muckdweller, Bluegill Raider, Bluegill Warrior, Bluegill Oracle, Mosshide Mistweaver, Mosshide Mystic, Mosshide Trapper, Mosshide Mongrel, Mosshide Gnoll, Fen Creeper, Red Whelp, Lost Whelp, Flamesnorting Whelp, Crimson Whelp */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (1026, 1024, 1028, 1418, 1027, 1029, 1009, 1013, 1011, 1008, 1007, 1040, 1042, 1043, 1044, 1069); + +/* WINTERSPRING */ +/* Cobalt Mageweaver, Chillwind Chimaera */ +UPDATE `creature_template` SET `ScriptName` = 'generic_creature' WHERE `entry` IN (7437, 7448); + + +/* EOF */ diff --git a/src/bindings/scripts/sql/scriptdev2_structure.sql b/src/bindings/scripts/sql/scriptdev2_structure.sql new file mode 100644 index 00000000000..7c20c216b6e --- /dev/null +++ b/src/bindings/scripts/sql/scriptdev2_structure.sql @@ -0,0 +1,105 @@ +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` ( +`id` int(11) unsigned NOT NULL auto_increment COMMENT 'Identifier', +`sound` int(11) unsigned NOT NULL default '0', +`type` int(11) unsigned NOT NULL default '0', +`language` int(11) unsigned NOT NULL default '0', +`text` varchar(255) NOT NULL default '', +`comment` varchar(255) NOT NULL default '', +PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Script Texts'; + + +DROP TABLE IF EXISTS `script_localized_texts`; +CREATE TABLE `script_localized_texts` ( +`id` int(11) unsigned NOT NULL auto_increment COMMENT 'Identifier', +`locale_1` varchar(255) NOT NULL default '', +`locale_2` varchar(255) NOT NULL default '', +`locale_3` varchar(255) NOT NULL default '', +`locale_4` varchar(255) NOT NULL default '', +`locale_5` varchar(255) NOT NULL default '', +`locale_6` varchar(255) NOT NULL default '', +`locale_7` varchar(255) NOT NULL default '', +`locale_8` varchar(255) NOT NULL default '', +`comment` varchar(255) NOT NULL default '' COMMENT 'Text Comment', +PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Localized Script Text'; + + +DROP TABLE IF EXISTS `sd2_db_version`; +CREATE TABLE `sd2_db_version` ( +`version` varchar(255) NOT NULL default '' COMMENT 'Database version string' +) ENGINE=MyISAM DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/src/bindings/scripts/system.cpp b/src/bindings/scripts/system.cpp new file mode 100644 index 00000000000..5133963daa3 --- /dev/null +++ b/src/bindings/scripts/system.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "precompiled.h" diff --git a/src/bindings/scripts/trinityscript.conf.dist b/src/bindings/scripts/trinityscript.conf.dist new file mode 100644 index 00000000000..97a59270f09 --- /dev/null +++ b/src/bindings/scripts/trinityscript.conf.dist @@ -0,0 +1,20 @@ +# ScriptDev2 Configuration file +# This file must be placed within the directory which holds mangosd.conf and realmd.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 +ScriptDev2DatabaseInfo = "127.0.0.1;3306;mangos;mangos;scriptdev2" + +# 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 \ No newline at end of file diff --git a/src/framework/Dynamic/FactoryHolder.h b/src/framework/Dynamic/FactoryHolder.h new file mode 100644 index 00000000000..7041c4ecbfa --- /dev/null +++ b/src/framework/Dynamic/FactoryHolder.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_FACTORY_HOLDER +#define MANGOS_FACTORY_HOLDER + +#include "Platform/Define.h" +#include "Utilities/TypeList.h" +#include "ObjectRegistry.h" +#include "Policies/SingletonImp.h" + +/** FactoryHolder holds a factory object of a specific type + */ +template +class MANGOS_DLL_DECL FactoryHolder +{ + public: + typedef ObjectRegistry, Key > FactoryHolderRegistry; + typedef MaNGOS::Singleton FactoryHolderRepository; + + FactoryHolder(Key k) : i_key(k) {} + virtual ~FactoryHolder() {} + inline Key key() const { return i_key; } + + void RegisterSelf(void) { FactoryHolderRepository::Instance().InsertItem(this, i_key); } + void DeregisterSelf(void) { FactoryHolderRepository::Instance().RemoveItem(this, false); } + + /// Abstract Factory create method + virtual T* Create(void *data = NULL) const = 0; + private: + Key i_key; +}; + +/** Permissible is a classic way of letting the object decide + * whether how good they handle things. This is not retricted + * to factory selectors. + */ +template +class Permissible +{ + public: + virtual ~Permissible() {} + virtual int Permit(const T *) const = 0; +}; +#endif diff --git a/src/framework/Dynamic/ObjectRegistry.h b/src/framework/Dynamic/ObjectRegistry.h new file mode 100644 index 00000000000..e0d885d2b9b --- /dev/null +++ b/src/framework/Dynamic/ObjectRegistry.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_OBJECTREGISTRY_H +#define MANGOS_OBJECTREGISTRY_H + +#include "Platform/Define.h" +#include "Utilities/HashMap.h" +#include "Policies/Singleton.h" + +#include +#include + +/** ObjectRegistry holds all registry item of the same type + */ +template +class MANGOS_DLL_DECL ObjectRegistry +{ + public: + typedef std::map RegistryMapType; + + /// Returns a registry item + const T* GetRegistryItem(Key key) const + { + typename RegistryMapType::const_iterator iter = i_registeredObjects.find(key); + return( iter == i_registeredObjects.end() ? NULL : iter->second ); + } + + /// Inserts a registry item + bool InsertItem(T *obj, Key key, bool override = false) + { + typename RegistryMapType::iterator iter = i_registeredObjects.find(key); + if( iter != i_registeredObjects.end() ) + { + if( !override ) + return false; + delete iter->second; + i_registeredObjects.erase(iter); + } + + i_registeredObjects[key] = obj; + return true; + } + + /// Removes a registry item + void RemoveItem(Key key, bool delete_object = true) + { + typename RegistryMapType::iterator iter = i_registeredObjects.find(key); + if( iter != i_registeredObjects.end() ) + { + if( delete_object ) + delete iter->second; + i_registeredObjects.erase(iter); + } + } + + /// Returns true if registry contains an item + bool HasItem(Key key) const + { + return (i_registeredObjects.find(key) != i_registeredObjects.end()); + } + + /// Inefficiently return a vector of registered items + unsigned int GetRegisteredItems(std::vector &l) const + { + unsigned int sz = l.size(); + l.resize(sz + i_registeredObjects.size()); + for(typename RegistryMapType::const_iterator iter = i_registeredObjects.begin(); iter != i_registeredObjects.end(); ++iter) + l[sz++] = iter->first; + return i_registeredObjects.size(); + } + + /// Return the map of registered items + RegistryMapType const &GetRegisteredItems() const + { + return i_registeredObjects; + } + + private: + RegistryMapType i_registeredObjects; + friend class MaNGOS::OperatorNew >; + + // protected for friend use since it should be a singleton + ObjectRegistry() {} + ~ObjectRegistry() + { + for(typename RegistryMapType::iterator iter=i_registeredObjects.begin(); iter != i_registeredObjects.end(); ++iter) + delete iter->second; + i_registeredObjects.clear(); + } +}; +#endif diff --git a/src/framework/GameSystem/Grid.h b/src/framework/GameSystem/Grid.h new file mode 100644 index 00000000000..f4a274a51a6 --- /dev/null +++ b/src/framework/GameSystem/Grid.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_GRID_H +#define MANGOS_GRID_H + +/* + @class Grid + Grid is a logical segment of the game world represented inside MaNGOS. + Grid is bind at compile time to a particular type of object which + we call it the object of interested. There are many types of loader, + specially, dynamic loader, static loader, or on-demand loader. There's + a subtle difference between dynamic loader and on-demand loader but + this is implementation specific to the loader class. From the + Grid's perspective, the loader meets its API requirement is suffice. +*/ + +#include "Platform/Define.h" +#include "Policies/ThreadingModel.h" +#include "TypeContainer.h" +#include "TypeContainerVisitor.h" + +// forward declaration +template class GridLoader; + +template +< +class ACTIVE_OBJECT, +class WORLD_OBJECT_TYPES, +class GRID_OBJECT_TYPES, +class ThreadModel = MaNGOS::SingleThreaded +> +class MANGOS_DLL_DECL Grid +{ + // allows the GridLoader to access its internals + template friend class GridLoader; + public: + + /** destructor to clean up its resources. This includes unloading the + grid if it has not been unload. + */ + ~Grid() {} + + /** an object of interested enters the grid + */ + template bool AddWorldObject(SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl) + { + return i_objects.template insert(hdl, obj); + } + + /** an object of interested exits the grid + */ + template bool RemoveWorldObject(SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl) + { + return i_objects.template remove(obj, hdl); + } + + /** Accessors: Returns a specific type of object in the WORDL_OBJECT_TYPES + */ + template const SPECIFIC_OBJECT* GetWorldObject(OBJECT_HANDLE hdl, SPECIFIC_OBJECT* fake) const { return i_objects.template find(hdl); } + template SPECIFIC_OBJECT* GetWorldObject(OBJECT_HANDLE hdl, SPECIFIC_OBJECT *fake) { return i_objects.template find(hdl, fake); } + + /** Refreshes/update the grid. This required for remote grids. + */ + void RefreshGrid(void) { /* TBI */} + + /** Locks a grid. Any object enters must wait until the grid is unlock. + */ + void LockGrid(void) { /* TBI */ } + + /** Unlocks the grid. + */ + void UnlockGrid(void) { /* TBI */ } + + /** Grid visitor for grid objects + */ + template void Visit(TypeContainerVisitor > &visitor) + { + visitor.Visit(i_container); + } + + /** Grid visitor for world objects + */ + template void Visit(TypeContainerVisitor > &visitor) + { + visitor.Visit(i_objects); + } + + /** Returns the number of object within the grid. + */ + unsigned int ActiveObjectsInGrid(void) const { return i_objects.template Count(); } + + /** Accessors: Returns a specific type of object in the GRID_OBJECT_TYPES + */ + template const SPECIFIC_OBJECT* GetGridObject(OBJECT_HANDLE hdl, SPECIFIC_OBJECT *fake) const { return i_container.template find(hdl, fake); } + template SPECIFIC_OBJECT* GetGridObject(OBJECT_HANDLE hdl, SPECIFIC_OBJECT *fake) { return i_container.template find(hdl, fake); } + + /** Inserts a container type object into the grid. + */ + template bool AddGridObject(SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl) { return i_container.template insert(hdl, obj); } + + /** Removes a containter type object from the grid + */ + template bool RemoveGridObject(SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl) { return i_container.template remove(obj, hdl); } + + private: + + typedef typename ThreadModel::Lock Guard; + typedef typename ThreadModel::VolatileType VolatileType; + + TypeMapContainer i_container; + TypeMapContainer i_objects; +}; +#endif diff --git a/src/framework/GameSystem/GridLoader.h b/src/framework/GameSystem/GridLoader.h new file mode 100644 index 00000000000..07982a613f5 --- /dev/null +++ b/src/framework/GameSystem/GridLoader.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_GRIDLOADER_H +#define MANGOS_GRIDLOADER_H + +/** + @class GridLoader + The GridLoader is working in conjuction with the Grid and responsible + for loading and unloading object-types (one or more) when objects + enters a grid. Unloading is scheduled and might be canceled if + an interested object re-enters. GridLoader does not do the actuall + loading and unloading but implements as a template pattern that + delicate its loading and unloading for the actualy loader and unloader. + GridLoader manages the grid (both local and remote). + */ + +#include "Platform/Define.h" +#include "Grid.h" +#include "TypeContainerVisitor.h" + +template +< +class ACTIVE_OBJECT, +class WORLD_OBJECT_TYPES, +class GRID_OBJECT_TYPES +> +class MANGOS_DLL_DECL GridLoader +{ + public: + + /** Loads the grid + */ + template + void Load(Grid &grid, LOADER &loader) + { + grid.LockGrid(); + loader.Load(grid); + grid.UnlockGrid(); + } + + /** Stop the grid + */ + template + void Stop(Grid &grid, STOPER &stoper) + { + grid.LockGrid(); + stoper.Stop(grid); + grid.UnlockGrid(); + } + /** Unloads the grid + */ + template + void Unload(Grid &grid, UNLOADER &unloader) + { + grid.LockGrid(); + unloader.Unload(grid); + grid.UnlockGrid(); + } +}; +#endif diff --git a/src/framework/GameSystem/GridRefManager.h b/src/framework/GameSystem/GridRefManager.h new file mode 100644 index 00000000000..fb402a1c6d3 --- /dev/null +++ b/src/framework/GameSystem/GridRefManager.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GRIDREFMANAGER +#define _GRIDREFMANAGER + +#include "Utilities/LinkedReference/RefManager.h" + +template +class GridReference; + +template +class GridRefManager : public RefManager, OBJECT> +{ + public: + typedef LinkedListHead::Iterator< GridReference > iterator; + + GridReference* getFirst() { return (GridReference*)RefManager, OBJECT>::getFirst(); } + GridReference* getLast() { return (GridReference*)RefManager, OBJECT>::getLast(); } + + iterator begin() { return iterator(getFirst()); } + iterator end() { return iterator(NULL); } + iterator rbegin() { return iterator(getLast()); } + iterator rend() { return iterator(NULL); } +}; +#endif diff --git a/src/framework/GameSystem/GridReference.h b/src/framework/GameSystem/GridReference.h new file mode 100644 index 00000000000..92d30e54ae1 --- /dev/null +++ b/src/framework/GameSystem/GridReference.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GRIDREFERENCE_H +#define _GRIDREFERENCE_H + +#include "Utilities/LinkedReference/Reference.h" + +template +class GridRefManager; + +template +class MANGOS_DLL_SPEC GridReference : public Reference, OBJECT> +{ + protected: + void targetObjectBuildLink() + { + // called from link() + this->getTarget()->insertFirst(this); + this->getTarget()->incSize(); + } + void targetObjectDestroyLink() + { + // called from unlink() + if(this->isValid()) this->getTarget()->decSize(); + } + void sourceObjectDestroyLink() + { + // called from invalidate() + this->getTarget()->decSize(); + } + public: + GridReference() : Reference, OBJECT>() {} + ~GridReference() { this->unlink(); } + GridReference *next() { return (GridReference*)Reference, OBJECT>::next(); } +}; +#endif diff --git a/src/framework/GameSystem/NGrid.h b/src/framework/GameSystem/NGrid.h new file mode 100644 index 00000000000..941a90282e2 --- /dev/null +++ b/src/framework/GameSystem/NGrid.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_NGRID_H +#define MANGOS_NGRID_H + +/** NGrid is nothing more than a wrapper of the Grid with an NxN cells + */ + +#include "GameSystem/Grid.h" +#include "GameSystem/GridReference.h" +#include "Timer.h" + +class GridInfo +{ +public: + GridInfo() : i_timer(0) {} + GridInfo(time_t expiry, bool unload = true ) : i_timer(expiry), i_unloadflag(unload) {} + const TimeTracker& getTimeTracker() const { return i_timer; } + bool getUnloadFlag() const { return i_unloadflag; } + void setUnloadFlag( bool pFlag) { i_unloadflag = pFlag; } + void setTimer(const TimeTracker& pTimer) { i_timer = pTimer; } + void ResetTimeTracker(time_t interval) { i_timer.Reset(interval); } + void UpdateTimeTracker(time_t diff) { i_timer.Update(diff); } + +private: + TimeTracker i_timer; + bool i_unloadflag; +}; + +typedef enum +{ + GRID_STATE_INVALID = 0, + GRID_STATE_ACTIVE = 1, + GRID_STATE_IDLE = 2, + GRID_STATE_REMOVAL= 3, + MAX_GRID_STATE = 4 +} grid_state_t; + +template +< +unsigned int N, +class ACTIVE_OBJECT, +class WORLD_OBJECT_TYPES, +class GRID_OBJECT_TYPES, +class ThreadModel = MaNGOS::SingleThreaded +> +class MANGOS_DLL_DECL NGrid +{ + public: + + typedef Grid GridType; + NGrid(uint32 id, int32 x, int32 y, time_t expiry, bool unload = true) : + i_gridId(id), i_cellstate(GRID_STATE_INVALID), i_x(x), i_y(y), i_GridObjectDataLoaded(false) + { + i_GridInfo = GridInfo(expiry, unload); + } + + const GridType& operator()(unsigned short x, unsigned short y) const { return i_cells[x][y]; } + GridType& operator()(unsigned short x, unsigned short y) { return i_cells[x][y]; } + + inline const uint32& GetGridId(void) const { return i_gridId; } + inline void SetGridId(const uint32 id) const { i_gridId = id; } + inline grid_state_t GetGridState(void) const { return i_cellstate; } + inline void SetGridState(grid_state_t s) { i_cellstate = s; } + inline int32 getX() const { return i_x; } + inline int32 getY() const { return i_y; } + + void link(GridRefManager >* pTo) + { + i_Reference.link(pTo, this); + } + bool isGridObjectDataLoaded() const { return i_GridObjectDataLoaded; } + void setGridObjectDataLoaded(bool pLoaded) { i_GridObjectDataLoaded = pLoaded; } + + GridInfo* getGridInfoRef() { return &i_GridInfo; } + const TimeTracker& getTimeTracker() const { return i_GridInfo.getTimeTracker(); } + bool getUnloadFlag() const { return i_GridInfo.getUnloadFlag(); } + void setUnloadFlag( bool pFlag) { i_GridInfo.setUnloadFlag(pFlag); } + void ResetTimeTracker(time_t interval) { i_GridInfo.ResetTimeTracker(interval); } + void UpdateTimeTracker(time_t diff) { i_GridInfo.UpdateTimeTracker(diff); } + + template void AddWorldObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl) + { + i_cells[x][y].AddWorldObject(obj, hdl); + } + + template void RemoveWorldObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl) + { + i_cells[x][y].RemoveWorldObject(obj, hdl); + } + + template void Visit(TypeContainerVisitor > &visitor) + { + for(unsigned int x=0; x < N; ++x) + for(unsigned int y=0; y < N; ++y) + i_cells[x][y].Visit(visitor); + } + + template void Visit(const uint32 &x, const uint32 &y, TypeContainerVisitor > &visitor) + { + i_cells[x][y].Visit(visitor); + } + + unsigned int ActiveObjectsInGrid(void) const + { + unsigned int count=0; + for(unsigned int x=0; x < N; ++x) + for(unsigned int y=0; y < N; ++y) + count += i_cells[x][y].ActiveObjectsInGrid(); + return count; + } + + template const SPECIFIC_OBJECT* GetGridObject(const uint32 x, const uint32 y, OBJECT_HANDLE hdl) const + { + return i_cells[x][y].template GetGridObject(hdl); + } + + template SPECIFIC_OBJECT* GetGridObject(const uint32 x, const uint32 y, OBJECT_HANDLE hdl) + { + return i_cells[x][y].template GetGridObject(hdl); + } + + template bool AddGridObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl) + { + return i_cells[x][y].AddGridObject(hdl, obj); + } + + template bool RemoveGridObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl) + { + return i_cells[x][y].RemoveGridObject(obj, hdl); + } + + private: + + uint32 i_gridId; + GridInfo i_GridInfo; + GridReference > i_Reference; + int32 i_x; + int32 i_y; + grid_state_t i_cellstate; + GridType i_cells[N][N]; + bool i_GridObjectDataLoaded; +}; +#endif diff --git a/src/framework/GameSystem/TypeContainer.h b/src/framework/GameSystem/TypeContainer.h new file mode 100644 index 00000000000..c07d8229a99 --- /dev/null +++ b/src/framework/GameSystem/TypeContainer.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_TYPECONTAINER_H +#define MANGOS_TYPECONTAINER_H + +/* + * Here, you'll find a series of containers that allow you to hold multiple + * types of object at the same time. + */ + +#include +#include +#include "Platform/Define.h" +#include "Utilities/TypeList.h" +#include "GameSystem/GridRefManager.h" + +/* + * @class ContainerMapList is a mulit-type container for map elements + * By itself its meaningless but collaborate along with TypeContainers, + * it become the most powerfully container in the whole system. + */ +template struct ContainerMapList +{ + //std::map _element; + GridRefManager _element; +}; + +template<> struct ContainerMapList /* nothing is in type null */ +{ +}; +template struct ContainerMapList > +{ + ContainerMapList _elements; + ContainerMapList _TailElements; +}; + +/* + * @class ContaierArrayList is a multi-type container for + * array of elements. + */ +template struct ContainerArrayList +{ + std::vector _element; +}; + +// termination condition +template<> struct ContainerArrayList {}; +// recursion +template struct ContainerArrayList > +{ + ContainerArrayList _elements; + ContainerArrayList _TailElements; +}; + +/* + * @class ContainerList is a simple list of different types of elements + * + */ +template struct ContainerList +{ + OBJECT _element; +}; + +/* TypeNull is underfined */ +template<> struct ContainerList {}; +template struct ContainerList > +{ + ContainerList _elements; + ContainerMapList _TailElements; +}; + +#include "TypeContainerFunctions.h" + +/* + * @class TypeMapContainer contains a fixed number of types and is + * determined at compile time. This is probably the most complicated + * class and do its simplest thing, that is, holds objects + * of different types. + */ + +template +class MANGOS_DLL_DECL TypeMapContainer +{ + public: + template size_t Count() const { return MaNGOS::Count(i_elements, (SPECIFIC_TYPE*)NULL); } + + template SPECIFIC_TYPE* find(OBJECT_HANDLE hdl, SPECIFIC_TYPE *fake) { return MaNGOS::Find(i_elements, hdl,fake); } + + /// find a specific type of object in the container + template const SPECIFIC_TYPE* find(OBJECT_HANDLE hdl, SPECIFIC_TYPE *fake) const { return MaNGOS::Find(i_elements, hdl,fake); } + + /// inserts a specific object into the container + template bool insert(OBJECT_HANDLE hdl, SPECIFIC_TYPE *obj) + { + SPECIFIC_TYPE* t = MaNGOS::Insert(i_elements, obj, hdl); + return (t != NULL); + } + + /// Removes the object from the container, and returns the removed object + template bool remove(SPECIFIC_TYPE* obj, OBJECT_HANDLE hdl) + { + SPECIFIC_TYPE* t = MaNGOS::Remove(i_elements, obj, hdl); + return (t != NULL); + } + + ContainerMapList & GetElements(void) { return i_elements; } + const ContainerMapList & GetElements(void) const { return i_elements;} + + private: + ContainerMapList i_elements; +}; +#endif diff --git a/src/framework/GameSystem/TypeContainerFunctions.h b/src/framework/GameSystem/TypeContainerFunctions.h new file mode 100644 index 00000000000..81a6a5f876b --- /dev/null +++ b/src/framework/GameSystem/TypeContainerFunctions.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TYPECONTAINER_FUNCTIONS_H +#define TYPECONTAINER_FUNCTIONS_H + +/* + * Here you'll find a list of helper functions to make + * the TypeContainer usefull. Without it, its hard + * to access or mutate the container. + */ + +#include "Platform/Define.h" +#include "Utilities/TypeList.h" +#include + +namespace MaNGOS +{ + /* ContainerMapList Helpers */ + // count functions + template size_t Count(const ContainerMapList &elements, SPECIFIC_TYPE* /*fake*/) + { + return elements._element.getSize(); + }; + + template size_t Count(const ContainerMapList &/*elements*/, SPECIFIC_TYPE* /*fake*/) + { + return 0; + } + + template size_t Count(const ContainerMapList &/*elements*/, SPECIFIC_TYPE* /*fake*/) + { + return 0; + } + + template size_t Count(const ContainerMapList >&elements, SPECIFIC_TYPE* fake) + { + return Count(elements._elements,fake); + } + + template size_t Count(const ContainerMapList >&elements, SPECIFIC_TYPE* fake) + { + return Count(elements._TailElements, fake); + } + + // non-const find functions + template SPECIFIC_TYPE* Find(ContainerMapList &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/) + { + //typename std::map::iterator iter = elements._element.find(hdl); + //return (iter == elements._element.end() ? NULL : iter->second); + return NULL; + }; + + template SPECIFIC_TYPE* Find(ContainerMapList &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/) + { + return NULL; // terminate recursion + } + + template SPECIFIC_TYPE* Find(ContainerMapList &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/) + { + return NULL; // this is a missed + } + + template SPECIFIC_TYPE* Find(ContainerMapList >&/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/) + { + //SPECIFIC_TYPE* t = Find(elements._elements, hdl,fake); + //return (t != NULL ? t :Find(elements._TailElements, hdl,fake)); + return NULL; + } + + // const find functions + template const SPECIFIC_TYPE* Find(const ContainerMapList &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/) + { + //typename SPECIFIC_TYPE::iterator iter = elements._element.find(hdl); + //return (iter == elements._element.end() ? NULL : iter->second); + return NULL; + }; + + template const SPECIFIC_TYPE* Find(const ContainerMapList &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/) + { + return NULL; + } + + template const SPECIFIC_TYPE* Find(const ContainerMapList &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/) + { + return NULL; + } + + template SPECIFIC_TYPE* Find(const ContainerMapList >&elements, OBJECT_HANDLE hdl, SPECIFIC_TYPE* fake) + { + SPECIFIC_TYPE* t = Find(elements._elements, hdl,fake); + if( t) + return t; + + return Find(elements._TailElement, hdl,fake); + } + + // non-const insert functions + template SPECIFIC_TYPE* Insert(ContainerMapList &elements, SPECIFIC_TYPE *obj, OBJECT_HANDLE /*hdl*/) + { + //elements._element[hdl] = obj; + obj->GetGridRef().link(&elements._element, obj); + return obj; + }; + + template SPECIFIC_TYPE* Insert(ContainerMapList &/*elements*/, SPECIFIC_TYPE * /*obj*/, OBJECT_HANDLE /*hdl*/) + { + return NULL; + } + + // this is a missed + template SPECIFIC_TYPE* Insert(ContainerMapList &/*elements*/, SPECIFIC_TYPE * /*obj*/, OBJECT_HANDLE /*hdl*/) + { + return NULL; // a missed + } + + // Recursion + template SPECIFIC_TYPE* Insert(ContainerMapList >&elements, SPECIFIC_TYPE *obj, OBJECT_HANDLE hdl) + { + SPECIFIC_TYPE* t= Insert(elements._elements, obj, hdl); + return (t != NULL ? t : Insert(elements._TailElements, obj, hdl)); + } + + // non-const remove method + template SPECIFIC_TYPE* Remove(ContainerMapList & /*elements*/, SPECIFIC_TYPE *obj, OBJECT_HANDLE /*hdl*/) + { + /*typename std::map::iterator iter = elements._element.find(hdl); + if( iter != elements._element.end() ) + { + SPECIFIC_TYPE* t = iter->second; + elements._element.erase(iter); + return t; + }*/ + obj->GetGridRef().unlink(); + return obj; + } + + template SPECIFIC_TYPE* Remove(ContainerMapList &/*elements*/, SPECIFIC_TYPE * /*obj*/, OBJECT_HANDLE /*hdl*/) + { + return NULL; + } + + // this is a missed + template SPECIFIC_TYPE* Remove(ContainerMapList &/*elements*/, SPECIFIC_TYPE * /*obj*/, OBJECT_HANDLE /*hdl*/) + { + return NULL; // a missed + } + + template SPECIFIC_TYPE* Remove(ContainerMapList > &elements, SPECIFIC_TYPE *obj, OBJECT_HANDLE hdl) + { + // The head element is bad + SPECIFIC_TYPE* t = Remove(elements._elements, obj, hdl); + return ( t != NULL ? t : Remove(elements._TailElements, obj, hdl) ); + } + +} +#endif diff --git a/src/framework/GameSystem/TypeContainerFunctionsPtr.h b/src/framework/GameSystem/TypeContainerFunctionsPtr.h new file mode 100644 index 00000000000..37ede0f6411 --- /dev/null +++ b/src/framework/GameSystem/TypeContainerFunctionsPtr.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TYPECONTAINER_FUNCTIONS_PTR_H +#define TYPECONTAINER_FUNCTIONS_PTR_H + +/* + * Here you'll find a list of helper functions to make + * the TypeContainer usefull. Without it, its hard + * to access or mutate the container. + */ + +#include "Platform/Define.h" +#include "Utilities/TypeList.h" +#include + +namespace MaNGOS +{ + /* ContainerMapList Helpers */ + // count functions + // template size_t Count(const ContainerMapList &elements, CountedPtr* /*fake*/) + // { + // return elements._element.size(); + // }; + // + // template size_t Count(const ContainerMapList &elements, CountedPtr* /*fake*/) + // { + // return 0; + // } + // + // template size_t Count(const ContainerMapList &elements, CountedPtr* /*fake*/) + // { + // return 0; + // } + // + // template size_t Count(const ContainerMapList >&elements, SPECIFIC_TYPE* fake) + // { + // return Count(elements._elements,fake); + // } + // + // template size_t Count(const ContainerMapList >&elements, SPECIFIC_TYPE* fake) + // { + // return Count(elements._TailElements, fake); + // } + + // non-const find functions + template CountedPtr& Find(ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + typename std::map >::iterator iter = elements._element.find(hdl); + return (iter == elements._element.end() ? NullPtr((SPECIFIC_TYPE*)NULL) : iter->second); + }; + + template CountedPtr& Find(ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + return NullPtr((SPECIFIC_TYPE*)NULL);// terminate recursion + } + + template CountedPtr& Find(ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + return NullPtr((SPECIFIC_TYPE*)NULL);// this is a missed + } + + template CountedPtr& Find(ContainerMapList >&elements, OBJECT_HANDLE hdl, CountedPtr* fake) + { + CountedPtr &t = Find(elements._elements, hdl,fake); + return (!t ? Find(elements._TailElements, hdl,fake) : t); + } + + // const find functions + template const CountedPtr& Find(const ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + typename CountedPtr::iterator iter = elements._element.find(hdl); + return (iter == elements._element.end() ? NullPtr((SPECIFIC_TYPE*)NULL) : iter->second); + }; + + template const CountedPtr& Find(const ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + return NullPtr((SPECIFIC_TYPE*)NULL); + } + + template const CountedPtr& Find(const ContainerMapList &elements, OBJECT_HANDLE hdl, CountedPtr* /*fake*/) + { + return NullPtr((SPECIFIC_TYPE*)NULL); + } + + template CountedPtr& Find(const ContainerMapList >&elements, OBJECT_HANDLE hdl, CountedPtr* fake) + { + CountedPtr &t = Find(elements._elements, hdl,fake); + if(!t) + t = Find(elements._TailElement, hdl,fake); + + return t; + } + + // non-const insert functions + template CountedPtr& Insert(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + elements._element[hdl] = obj; + return obj; + }; + + template CountedPtr& Insert(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + return NullPtr((SPECIFIC_TYPE*)NULL); + } + + // this is a missed + template CountedPtr& Insert(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + return NullPtr((SPECIFIC_TYPE*)NULL);// a missed + } + + // Recursion + template CountedPtr& Insert(ContainerMapList >&elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + CountedPtr &t= Insert(elements._elements, obj, hdl); + return (!t ? Insert(elements._TailElements, obj, hdl) : t); + } + + // non-const remove method + template bool Remove(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + typename std::map >::iterator iter = elements._element.find(hdl); + if( iter != elements._element.end() ) + { + elements._element.erase(iter); + return true; + } + + return false; // found... terminate the search + } + + template bool Remove(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + return false; + } + + // this is a missed + template bool Remove(ContainerMapList &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + return false; + } + + template bool Remove(ContainerMapList > &elements, CountedPtr &obj, OBJECT_HANDLE hdl) + { + // The head element is bad + bool t = Remove(elements._elements, obj, hdl); + return ( !t ? Remove(elements._TailElements, obj, hdl) : t ); + } + +} +#endif diff --git a/src/framework/GameSystem/TypeContainerVisitor.h b/src/framework/GameSystem/TypeContainerVisitor.h new file mode 100644 index 00000000000..8920b6d14d9 --- /dev/null +++ b/src/framework/GameSystem/TypeContainerVisitor.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_TYPECONTAINERVISITOR_H +#define MANGOS_TYPECONTAINERVISITOR_H + +/* + * @class TypeContainerVisitor is implemented as a visitor pattern. It is + * a visitor to the TypeContainerList or TypeContainerMapList. The visitor has + * to overload its types as a visit method is called. + */ + +#include "Platform/Define.h" +#include "TypeContainer.h" + +// forward declaration +template class TypeContainerVisitor; + +// visitor helper +template void VisitorHelper(VISITOR &v, TYPE_CONTAINER &c) +{ + v.Visit(c); +}; + +// terminate condition for container list +template void VisitorHelper(VISITOR &v, ContainerList &c) +{ +} + +template void VisitorHelper(VISITOR &v, ContainerList &c) +{ + v.Visit(c._element); +} + +// recursion for container list +template void VisitorHelper(VISITOR &v, ContainerList > &c) +{ + VisitorHelper(v, c._elements); + VisitorHelper(v, c._TailElements); +} + +// terminate condition container map list +template void VisitorHelper(VISITOR &/*v*/, ContainerMapList &/*c*/) +{ +} + +template void VisitorHelper(VISITOR &v, ContainerMapList &c) +{ + v.Visit(c._element); +} + +// recursion container map list +template void VisitorHelper(VISITOR &v, ContainerMapList > &c) +{ + VisitorHelper(v, c._elements); + VisitorHelper(v, c._TailElements); +} + +// array list +template void VisitorHelper(VISITOR &v, ContainerArrayList &c) +{ + v.Visit(c._element); +} + +template void VisitorHelper(VISITOR &/*v*/, ContainerArrayList &/*c*/) +{ +} + +// recursion +template void VisitorHelper(VISITOR &v, ContainerArrayList > &c) +{ + VisitorHelper(v, c._elements); + VisitorHelper(v, c._TailElements); +} + +// for TypeMapContainer +template void VisitorHelper(VISITOR &v, TypeMapContainer &c) +{ + VisitorHelper(v, c.GetElements()); +} + +template +class MANGOS_DLL_DECL TypeContainerVisitor +{ + public: + TypeContainerVisitor(VISITOR &v) : i_visitor(v) {} + + void Visit(TYPE_CONTAINER &c) + { + VisitorHelper(i_visitor, c); + } + + void Visit(const TYPE_CONTAINER &c) const + { + VisitorHelper(i_visitor, c); + } + + private: + VISITOR &i_visitor; +}; +#endif diff --git a/src/framework/Makefile.am b/src/framework/Makefile.am new file mode 100644 index 00000000000..aab4f653c41 --- /dev/null +++ b/src/framework/Makefile.am @@ -0,0 +1,64 @@ +# Copyright (C) 2005-2008 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## CPP flags for includes, defines, etc. +AM_CPPFLAGS = -I$(srcdir) + +## Build MaNGOS framework library as convenience library. +# libMaNGOSScript shared library will later be reused by world server daemon. +noinst_LIBRARIES = libmangosframework.a +libmangosframework_a_SOURCES = \ + Policies/ObjectLifeTime.cpp \ + Utilities/EventProcessor.cpp + +## Additional files to include when running 'make dist' +# Source and header files for the Framework. +EXTRA_DIST = \ + Dynamic/FactoryHolder.h \ + Dynamic/ObjectRegistry.h \ + GameSystem/Grid.h \ + GameSystem/GridLoader.h \ + GameSystem/GridRefManager.h \ + GameSystem/GridReference.h \ + GameSystem/NGrid.h \ + GameSystem/TypeContainer.h \ + GameSystem/TypeContainerFunctions.h \ + GameSystem/TypeContainerFunctionsPtr.h \ + GameSystem/TypeContainerVisitor.h \ + Network/SocketDefines.h \ + Platform/CompilerDefs.h \ + Platform/Define.h \ + Policies/CreationPolicy.h \ + Policies/ObjectLifeTime.h \ + Policies/Singleton.h \ + Policies/SingletonImp.h \ + Policies/ThreadingModel.h \ + Utilities/CountedReference/Reference.h \ + Utilities/CountedReference/ReferenceHolder.h \ + Utilities/CountedReference/ReferenceImpl.h \ + Utilities/LinkedReference/RefManager.h \ + Utilities/LinkedReference/Reference.h \ + Utilities/ByteConverter.h \ + Utilities/Callback.h \ + Utilities/EventProcessor.h \ + Utilities/HashMap.h \ + Utilities/LinkedList.h \ + Utilities/TypeList.h + diff --git a/src/framework/Network/SocketDefines.h b/src/framework/Network/SocketDefines.h new file mode 100644 index 00000000000..5b03758815b --- /dev/null +++ b/src/framework/Network/SocketDefines.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_SOCKETDEFINES_H +#define MANGOS_SOCKETDEFINES_H + +#ifdef WIN32 + +/* Windows socket definitions + */ +#define FD_SETSIZE 1024 +#include +#include + +typedef SOCKET SocketHandle; +typedef fd_set SelectSet; + +#else + +/* The unix socket definitions + */ +#include +#include +#ifdef __APPLE_CC__ +#include +#endif + +typedef int SocketHandle; +typedef fd_set SelectSet; +#endif +#endif diff --git a/src/framework/Platform/CompilerDefs.h b/src/framework/Platform/CompilerDefs.h new file mode 100644 index 00000000000..c0f659d55c4 --- /dev/null +++ b/src/framework/Platform/CompilerDefs.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_COMPILERDEFS_H +#define MANGOS_COMPILERDEFS_H + +#define PLATFORM_WINDOWS 0 +#define PLATFORM_UNIX 1 +#define PLATFORM_APPLE 2 +#define PLATFORM_INTEL 3 + +// must be first (win 64 also define WIN32) +#if defined( _WIN64 ) +# define PLATFORM PLATFORM_WINDOWS +#elif defined( __WIN32__ ) || defined( WIN32 ) || defined( _WIN32 ) +# define PLATFORM PLATFORM_WINDOWS +#elif defined( __APPLE_CC__ ) +# define PLATFORM PLATFORM_APPLE +#elif defined( __INTEL_COMPILER ) +# define PLATFORM PLATFORM_INTEL +#else +# define PLATFORM PLATFORM_UNIX +#endif + +#define COMPILER_MICROSOFT 0 +#define COMPILER_GNU 1 +#define COMPILER_BORLAND 2 +#define COMPILER_INTEL 3 + +#ifdef _MSC_VER +# define COMPILER COMPILER_MICROSOFT +#elif defined( __BORLANDC__ ) +# define COMPILER COMPILER_BORLAND +#elif defined( __INTEL_COMPILER ) +# define COMPILER COMPILER_INTEL +#elif defined( __GNUC__ ) +# define COMPILER COMPILER_GNU +#else +# pragma error "FATAL ERROR: Unknown compiler." +#endif + +#if COMPILER == COMPILER_MICROSOFT +# pragma warning( disable : 4267 ) // conversion from 'size_t' to 'int', possible loss of data +# pragma warning( disable : 4786 ) // identifier was truncated to '255' characters in the debug information +#endif +#endif diff --git a/src/framework/Platform/Define.h b/src/framework/Platform/Define.h new file mode 100644 index 00000000000..268cc4aab1b --- /dev/null +++ b/src/framework/Platform/Define.h @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_DEFINE_H +#define MANGOS_DEFINE_H + +#include "Platform/CompilerDefs.h" +#include + +/* Endian detection code from sha2.c: +------------------------------------------------------------------------- +Copyright (c) 2001, Dr Brian Gladman , Worcester, UK. +All rights reserved. + +TERMS + +Redistribution and use in source and binary forms, with or without +modification, are permitted subject to the following conditions: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. The copyright holder's name must not be used to endorse or promote +any products derived from this software without his specific prior +written permission. + +This software is provided 'as is' with no express or implied warranties +of correctness or fitness for purpose. +------------------------------------------------------------------------- +*/ + +/* 1. PLATFORM SPECIFIC INCLUDES */ + +#if defined(__GNU_LIBRARY__) +# include +# include +#elif defined(__CRYPTLIB__) +# if defined( INC_ALL ) +# include "crypt.h" +# elif defined( INC_CHILD ) +# include "../crypt.h" +# else +# include "crypt.h" +# endif +# if defined(DATA_LITTLEENDIAN) +# define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN +# else +# define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN +# endif +#elif defined(_MSC_VER) +# include +#elif !defined(WIN32) +# include +# if !defined (_ENDIAN_H) +# include +# else +# include _ENDIAN_H +# endif +#endif + +/* 2. BYTE ORDER IN 32-BIT WORDS + +To obtain the highest speed on processors with 32-bit words, this code +needs to determine the order in which bytes are packed into such words. +The following block of code is an attempt to capture the most obvious +ways in which various environemnts specify their endian definitions. +It may well fail, in which case the definitions will need to be set by +editing at the points marked **** EDIT HERE IF NECESSARY **** below. +*/ + +#define MANGOS_LITTLEENDIAN 0 +#define MANGOS_BIGENDIAN 1 + +#if !defined(MANGOS_ENDIAN) +# if defined(LITTLE_ENDIAN) || defined(BIG_ENDIAN) +# if defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) +# if defined(BYTE_ORDER) +# if (BYTE_ORDER == LITTLE_ENDIAN) +# define MANGOS_ENDIAN MANGOS_LITTLEENDIAN +# elif (BYTE_ORDER == BIG_ENDIAN) +# define MANGOS_ENDIAN MANGOS_BIGENDIAN +# endif +# endif +# elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) +# define MANGOS_ENDIAN MANGOS_LITTLEENDIAN +# elif !defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) +# define MANGOS_ENDIAN MANGOS_BIGENDIAN +# endif +# elif defined(_LITTLE_ENDIAN) || defined(_BIG_ENDIAN) +# if defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN) +# if defined(_BYTE_ORDER) +# if (_BYTE_ORDER == _LITTLE_ENDIAN) +# define MANGOS_ENDIAN MANGOS_LITTLEENDIAN +# elif (_BYTE_ORDER == _BIG_ENDIAN) +# define MANGOS_ENDIAN MANGOS_BIGENDIAN +# endif +# endif +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define MANGOS_ENDIAN MANGOS_LITTLE_ENDIAN +# elif !defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN) +# define MANGOS_ENDIAN MANGOS_BIGENDIAN +# endif +# elif 0 /* **** EDIT HERE IF NECESSARY **** */ +# define MANGOS_ENDIAN MANGOS_LITTLEENDIAN +# elif 0 /* **** EDIT HERE IF NECESSARY **** */ +# define MANGOS_ENDIAN MANGOS_BIGENDIAN +# elif (('1234' >> 24) == '1') +# define MANGOS_ENDIAN MANGOS_LITTLEENDIAN +# elif (('4321' >> 24) == '1') +# define MANGOS_ENDIAN MANGOS_BIGENDIAN +# else +# define MANGOS_ENDIAN MANGOS_LITTLEENDIAN +# endif +#endif + +/* End of Endian detection code from sha2.c: */ + +#if PLATFORM == PLATFORM_WINDOWS +#define MANGOS_EXPORT __declspec(dllexport) +#define MANGOS_LIBRARY_HANDLE HMODULE +#define MANGOS_LOAD_LIBRARY(a) LoadLibrary(a) +#define MANGOS_CLOSE_LIBRARY FreeLibrary +#define MANGOS_GET_PROC_ADDR GetProcAddress +#define MANGOS_IMPORT __cdecl +#define MANGOS_SCRIPT_EXT ".dll" +#define MANGOS_SCRIPT_NAME "TrinityScript" +#else +#define MANGOS_LIBRARY_HANDLE void* +#define MANGOS_EXPORT export +#define MANGOS_LOAD_LIBRARY(a) dlopen(a,RTLD_NOW) +#define MANGOS_CLOSE_LIBRARY dlclose +#define MANGOS_GET_PROC_ADDR dlsym + +#if defined(__APPLE_CC__) && defined(BIG_ENDIAN) +#define MANGOS_IMPORT __attribute__ ((longcall)) +#else +#define MANGOS_IMPORT __attribute__ ((cdecl)) +#endif + +#define MANGOS_SCRIPT_EXT ".so" +#define MANGOS_SCRIPT_NAME "libtrinityscript" +#endif + +#ifdef WIN32 +#ifdef MANGOS_WIN32_DLL_IMPORT + +#define MANGOS_DLL_DECL __declspec(dllimport) +#else +#ifdef MANGOS_WIND_DLL_EXPORT +#define MANGOS_DLL_DECL __declspec(dllexport) +#else +#define MANGOS_DLL_DECL +#endif +#endif + +#else +#define MANGOS_DLL_DECL +#endif + +#ifndef DEBUG +#define MANGOS_INLINE inline +#else +#ifndef MANGOS_DEBUG +#define MANGOS_DEBUG +#endif +#define MANGOS_INLINE +#endif + +#if COMPILER == COMPILER_MICROSOFT +typedef __int64 int64; +typedef __int32 int32; +typedef __int16 int16; +typedef __int8 int8; +typedef unsigned __int64 uint64; +typedef unsigned __int32 uint32; +typedef unsigned __int16 uint16; +typedef unsigned __int8 uint8; +#else +typedef __int64_t int64; +typedef __int32_t int32; +typedef __int16_t int16; +typedef __int8_t int8; +typedef __uint64_t uint64; +typedef __uint32_t uint32; +typedef __uint16_t uint16; +typedef __uint8_t uint8; +typedef uint16 WORD; +typedef uint32 DWORD; +#endif +typedef uint64 OBJECT_HANDLE; + +#if PLATFORM == PLATFORM_WINDOWS +# define MANGOS_DLL_SPEC __declspec(dllexport) +# ifndef DECLSPEC_NORETURN +# define DECLSPEC_NORETURN __declspec(noreturn) +# endif +#else +# define MANGOS_DLL_SPEC +# define DECLSPEC_NORETURN +#endif + +#if COMPILER == COMPILER_GNU +# define ATTR_NORETURN __attribute__((noreturn)) +# define ATTR_PRINTF(F,V) __attribute__ ((format (printf, F, V))) +#else +# define ATTR_NORETURN +# define ATTR_PRINTF(F,V) +#endif + +#endif diff --git a/src/framework/Policies/CreationPolicy.h b/src/framework/Policies/CreationPolicy.h new file mode 100644 index 00000000000..20419982965 --- /dev/null +++ b/src/framework/Policies/CreationPolicy.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CREATIONPOLICY_H +#define MANGOS_CREATIONPOLICY_H + +#include +#include "Platform/Define.h" + +namespace MaNGOS +{ + /** + * OperatorNew policy creates an object on the heap using new. + */ + template + class MANGOS_DLL_DECL OperatorNew + { + public: + static T* Create(void) { return (new T); } + static void Destroy(T *obj) { delete obj; } + }; + + /** + * LocalStaticCreation policy creates an object on the stack + * the first time call Create. + */ + template + class MANGOS_DLL_DECL LocalStaticCreation + { + union MaxAlign + { + char t_[sizeof(T)]; + short int shortInt_; + int int_; + long int longInt_; + float float_; + double double_; + long double longDouble_; + struct Test; + int Test::* pMember_; + int (Test::*pMemberFn_)(int); + }; + public: + static T* Create(void) + { + static MaxAlign si_localStatic; + return new(&si_localStatic) T; + } + + static void Destroy(T *obj) { obj->~T(); } + }; + + /** + * CreateUsingMalloc by pass the memory manger. + */ + template + class MANGOS_DLL_DECL CreateUsingMalloc + { + public: + static T* Create() + { + void* p = ::malloc(sizeof(T)); + if (!p) return 0; + return new(p) T; + } + + static void Destroy(T* p) + { + p->~T(); + ::free(p); + } + }; + + /** + * CreateOnCallBack creates the object base on the call back. + */ + template + class MANGOS_DLL_DECL CreateOnCallBack + { + public: + static T* Create() + { + return CALL_BACK::createCallBack(); + } + + static void Destroy(T *p) + { + CALL_BACK::destroyCallBack(p); + } + }; +} +#endif diff --git a/src/framework/Policies/ObjectLifeTime.cpp b/src/framework/Policies/ObjectLifeTime.cpp new file mode 100644 index 00000000000..af40edc1274 --- /dev/null +++ b/src/framework/Policies/ObjectLifeTime.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "ObjectLifeTime.h" + +namespace MaNGOS +{ + extern "C" void external_wrapper(void *p) + { + std::atexit( (void (*)())p ); + } + + void MANGOS_DLL_SPEC at_exit( void (*func)() ) + { + external_wrapper((void*)func); + } +} diff --git a/src/framework/Policies/ObjectLifeTime.h b/src/framework/Policies/ObjectLifeTime.h new file mode 100644 index 00000000000..c47daaca339 --- /dev/null +++ b/src/framework/Policies/ObjectLifeTime.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_OBJECTLIFETIME_H +#define MANGOS_OBJECTLIFETIME_H + +#include +#include "Platform/Define.h" + +typedef void (* Destroyer)(void); + +namespace MaNGOS +{ + void MANGOS_DLL_SPEC at_exit( void (*func)() ); + + template + class MANGOS_DLL_DECL ObjectLifeTime + { + public: + inline static void ScheduleCall(void (*destroyer)() ) + { + at_exit( destroyer ); + } + + DECLSPEC_NORETURN static void OnDeadReference(void) ATTR_NORETURN; + + }; + + template + inline void ObjectLifeTime::OnDeadReference(void)// We don't handle Dead Reference for now + { + throw std::runtime_error("Dead Reference"); + } +} +#endif diff --git a/src/framework/Policies/Singleton.h b/src/framework/Policies/Singleton.h new file mode 100644 index 00000000000..b70c5486dff --- /dev/null +++ b/src/framework/Policies/Singleton.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_SINGLETON_H +#define MANGOS_SINGLETON_H + +/** + * @brief class Singleton + */ + +#include "CreationPolicy.h" +#include "ThreadingModel.h" +#include "ObjectLifeTime.h" + +namespace MaNGOS +{ + template + < + typename T, + class ThreadingModel = MaNGOS::SingleThreaded, + class CreatePolicy = MaNGOS::OperatorNew, + class LifeTimePolicy = MaNGOS::ObjectLifeTime + > + class MANGOS_DLL_DECL Singleton + { + public: + static T& Instance(); + + protected: + Singleton() {}; + + private: + + // Prohibited actions...this does not prevent hijacking. + Singleton(const Singleton &); + Singleton& operator=(const Singleton &); + + // Singleton Helpers + static void DestroySingleton(); + + // data structure + typedef typename ThreadingModel::Lock Guard; + static T *si_instance; + static bool si_destroyed; + }; +} +#endif diff --git a/src/framework/Policies/SingletonImp.h b/src/framework/Policies/SingletonImp.h new file mode 100644 index 00000000000..ce0a4403472 --- /dev/null +++ b/src/framework/Policies/SingletonImp.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_SINGLETONIMPL_H +#define MANGOS_SINGLETONIMPL_H + +#include "Singleton.h" + +// avoid the using namespace here cuz +// its a .h file afterall + +template +< +typename T, +class ThreadingModel, +class CreatePolicy, +class LifeTimePolicy +> +T& +MaNGOS::Singleton::Instance() +{ + if( !si_instance ) + { + // double-checked Locking pattern + Guard(); + if( !si_instance ) + { + if( si_destroyed ) + { + si_destroyed = false; + LifeTimePolicy::OnDeadReference(); + } + si_instance = CreatePolicy::Create(); + LifeTimePolicy::ScheduleCall(&DestroySingleton); + } + } + + return *si_instance; +} + +template +< +typename T, +class ThreadingModel, +class CreatePolicy, +class LifeTimePolicy +> +void +MaNGOS::Singleton::DestroySingleton() +{ + CreatePolicy::Destroy(si_instance); + si_instance = NULL; + si_destroyed = true; +} + +#define INSTANTIATE_SINGLETON_1(TYPE) \ + template class MANGOS_DLL_DECL MaNGOS::Singleton, MaNGOS::OperatorNew, MaNGOS::ObjectLifeTime >; \ + template<> TYPE* MaNGOS::Singleton, MaNGOS::OperatorNew, MaNGOS::ObjectLifeTime >::si_instance = 0; \ + template<> bool MaNGOS::Singleton, MaNGOS::OperatorNew, MaNGOS::ObjectLifeTime >::si_destroyed = false + +#define INSTANTIATE_SINGLETON_2(TYPE, THREADINGMODEL) \ + template class MANGOS_DLL_DECL MaNGOS::Singleton, MaNGOS::ObjectLifeTime >; \ + template<> TYPE* MaNGOS::Singleton, MaNGOS::ObjectLifeTime >::si_instance = 0; \ + template<> bool MaNGOS::Singleton, MaNGOS::ObjectLifeTime >::si_destroyed = false + +#define INSTANTIATE_SINGLETON_3(TYPE, THREADINGMODEL, CREATIONPOLICY ) \ + template class MANGOS_DLL_DECL MaNGOS::Singleton >; \ + template<> TYPE* MaNGOS::Singleton >::si_instance = 0; \ + template<> bool MaNGOS::Singleton >::si_destroyed = false + +#define INSTANTIATE_SINGLETON_4(TYPE, THREADINGMODEL, CREATIONPOLICY, OBJECTLIFETIME) \ + template class MANGOS_DLL_DECL MaNGOS::Singleton; \ + template<> TYPE* MaNGOS::Singleton::si_instance = 0; \ + template<> bool MaNGOS::Singleton::si_destroyed = false +#endif diff --git a/src/framework/Policies/ThreadingModel.h b/src/framework/Policies/ThreadingModel.h new file mode 100644 index 00000000000..8175b3f82af --- /dev/null +++ b/src/framework/Policies/ThreadingModel.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_THREADINGMODEL_H +#define MANGOS_THREADINGMODEL_H + +/** + * @class ThreadingModel + * + */ + +#include "Platform/Define.h" + +namespace MaNGOS +{ + inline void Guard(void *) {} + + template class MANGOS_DLL_DECL GeneralLock + { + public: + GeneralLock(MUTEX &m) : i_mutex(m) + { + i_mutex.acquire(); + } + + ~GeneralLock() + { + i_mutex.release(); + } + private: + GeneralLock(const GeneralLock &); + GeneralLock& operator=(const GeneralLock &); + MUTEX &i_mutex; + }; + + template + class MANGOS_DLL_DECL SingleThreaded + { + public: + + struct Lock // empty object + { + Lock() {} + Lock(const T &) {} + Lock(const SingleThreaded &) // for single threaded we ignore this + { + } + }; + + typedef T VolatileType; + }; + + // object level lockable + template + class MANGOS_DLL_DECL ObjectLevelLockable + { + public: + ObjectLevelLockable() : i_mtx() {} + + friend class Lock; + + class Lock + { + public: + Lock(ObjectLevelLockable &host) : i_lock(host.i_mtx) + { + } + + private: + GeneralLock i_lock; + }; + + typedef volatile T VolatileType; + + private: + // prevent the compiler creating a copy construct + ObjectLevelLockable(const ObjectLevelLockable &); + ObjectLevelLockable& operator=(const ObjectLevelLockable &); + + MUTEX i_mtx; + }; + + template + class MANGOS_DLL_DECL ClassLevelLockable + { + public: + class Lock; + friend class Lock; + typedef volatile T VolatileType; + + ClassLevelLockable() {} + + class Lock + { + public: + Lock(T& /*host*/) { ClassLevelLockable::si_mtx.acquire(); } + Lock(ClassLevelLockable &) { ClassLevelLockable::si_mtx.acquire(); } + Lock() { ClassLevelLockable::si_mtx.acquire(); } + ~Lock() { ClassLevelLockable::si_mtx.release(); } + }; + + private: + static MUTEX si_mtx; + }; + +} + +template MUTEX MaNGOS::ClassLevelLockable::si_mtx; + +#define INSTANTIATE_CLASS_MUTEX(CTYPE,MUTEX) \ + template class MANGOS_DLL_DECL MaNGOS::ClassLevelLockable +#endif diff --git a/src/framework/Utilities/ByteConverter.h b/src/framework/Utilities/ByteConverter.h new file mode 100644 index 00000000000..fade287ed0a --- /dev/null +++ b/src/framework/Utilities/ByteConverter.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_BYTECONVERTER_H +#define MANGOS_BYTECONVERTER_H + +/** ByteConverter reverse your byte order. This is use + for cross platform where they have different endians. + */ + +#include +#include + +namespace ByteConverter +{ + template + inline void convert(char *val) + { + std::swap(*val, *(val + T - 1)); + convert(val + 1); + } + + template<> inline void convert<0>(char *) {} + template<> inline void convert<1>(char *) {} // ignore central byte + + template inline void apply(T *val) + { + convert((char *)(val)); + } +} + +#if MANGOS_ENDIAN == MANGOS_BIGENDIAN +template inline void EndianConvert(T& val) { ByteConverter::apply(&val); } +#else +template inline void EndianConvert(T&) { } +#endif + +template inline void EndianConvert(T*) { } +inline void EndianConvert(uint8&) { } +inline void EndianConvert( int8&) { } + +#endif diff --git a/src/framework/Utilities/Callback.h b/src/framework/Utilities/Callback.h new file mode 100644 index 00000000000..d794821a892 --- /dev/null +++ b/src/framework/Utilities/Callback.h @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CALLBACK_H +#define MANGOS_CALLBACK_H + +/// ------------ BASE CLASSES ------------ + +namespace MaNGOS +{ + template < class Class, typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void, typename ParamType4 = void > + class _Callback + { + protected: + typedef void (Class::*Method)(ParamType1, ParamType2, ParamType3, ParamType4); + Class *m_object; + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + ParamType3 m_param3; + ParamType4 m_param4; + void _Execute() { (m_object->*m_method)(m_param1, m_param2, m_param3, m_param4); } + public: + _Callback(Class *object, Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3, ParamType4 param4) + : m_object(object), m_method(method), m_param1(param1), m_param2(param2), m_param3(param3), m_param4(param4) {} + _Callback(_Callback < Class, ParamType1, ParamType2, ParamType3, ParamType4> const& cb) + : m_object(cb.object), m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2), m_param3(cb.m_param3), m_param4(cb.m_param4) {} + }; + + template < class Class, typename ParamType1, typename ParamType2, typename ParamType3 > + class _Callback < Class, ParamType1, ParamType2, ParamType3 > + { + protected: + typedef void (Class::*Method)(ParamType1, ParamType2, ParamType3); + Class *m_object; + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + ParamType3 m_param3; + void _Execute() { (m_object->*m_method)(m_param1, m_param2, m_param3); } + public: + _Callback(Class *object, Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3) + : m_object(object), m_method(method), m_param1(param1), m_param2(param2) {} + _Callback(_Callback < Class, ParamType1, ParamType2, ParamType3 > const& cb) + : m_object(cb.object), m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2), m_param3(cb.m_param3) {} + }; + + template < class Class, typename ParamType1, typename ParamType2 > + class _Callback < Class, ParamType1, ParamType2 > + { + protected: + typedef void (Class::*Method)(ParamType1, ParamType2); + Class *m_object; + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + void _Execute() { (m_object->*m_method)(m_param1, m_param2); } + public: + _Callback(Class *object, Method method, ParamType1 param1, ParamType2 param2) + : m_object(object), m_method(method), m_param1(param1), m_param2(param2) {} + _Callback(_Callback < Class, ParamType1, ParamType2 > const& cb) + : m_object(cb.m_object), m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2) {} + }; + + template < class Class, typename ParamType1 > + class _Callback < Class, ParamType1 > + { + protected: + typedef void (Class::*Method)(ParamType1); + Class *m_object; + Method m_method; + ParamType1 m_param1; + void _Execute() { (m_object->*m_method)(m_param1); } + public: + _Callback(Class *object, Method method, ParamType1 param1) + : m_object(object), m_method(method), m_param1(param1) {} + _Callback(_Callback < Class, ParamType1 > const& cb) + : m_object(cb.m_object), m_method(cb.m_method), m_param1(cb.m_param1) {} + }; + + template < class Class > + class _Callback < Class > + { + protected: + typedef void (Class::*Method)(); + Class *m_object; + Method m_method; + void _Execute() { (m_object->*m_method)(); } + public: + _Callback(Class *object, Method method) + : m_object(object), m_method(method) {} + _Callback(_Callback < Class > const& cb) + : m_object(cb.m_object), m_method(cb.m_method) {} + }; + + /// ---- Statics ---- + + template < typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void, typename ParamType4 = void > + class _SCallback + { + protected: + typedef void (*Method)(ParamType1, ParamType2, ParamType3, ParamType4); + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + ParamType3 m_param3; + ParamType4 m_param4; + void _Execute() { (*m_method)(m_param1, m_param2, m_param3, m_param4); } + public: + _SCallback(Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3, ParamType4 param4) + : m_method(method), m_param1(param1), m_param2(param2), m_param3(param3), m_param4(param4) {} + _SCallback(_SCallback < ParamType1, ParamType2, ParamType3, ParamType4> const& cb) + : m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2), m_param3(cb.m_param3), m_param4(cb.m_param4) {} + }; + + template < typename ParamType1, typename ParamType2, typename ParamType3 > + class _SCallback < ParamType1, ParamType2, ParamType3 > + { + protected: + typedef void (*Method)(ParamType1, ParamType2, ParamType3); + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + ParamType3 m_param3; + void _Execute() { (*m_method)(m_param1, m_param2, m_param3); } + public: + _SCallback(Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3) + : m_method(method), m_param1(param1), m_param2(param2) {} + _SCallback(_SCallback < ParamType1, ParamType2, ParamType3 > const& cb) + : m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2), m_param3(cb.m_param3) {} + }; + + template < typename ParamType1, typename ParamType2 > + class _SCallback < ParamType1, ParamType2 > + { + protected: + typedef void (*Method)(ParamType1, ParamType2); + Method m_method; + ParamType1 m_param1; + ParamType2 m_param2; + void _Execute() { (*m_method)(m_param1, m_param2); } + public: + _SCallback(Method method, ParamType1 param1, ParamType2 param2) + : m_method(method), m_param1(param1), m_param2(param2) {} + _SCallback(_SCallback < ParamType1, ParamType2 > const& cb) + : m_method(cb.m_method), m_param1(cb.m_param1), m_param2(cb.m_param2) {} + }; + + template < typename ParamType1 > + class _SCallback < ParamType1 > + { + protected: + typedef void (*Method)(ParamType1); + Method m_method; + ParamType1 m_param1; + void _Execute() { (*m_method)(m_param1); } + public: + _SCallback(Method method, ParamType1 param1) + : m_method(method), m_param1(param1) {} + _SCallback(_SCallback < ParamType1 > const& cb) + : m_method(cb.m_method), m_param1(cb.m_param1) {} + }; + + template < > + class _SCallback < > + { + protected: + typedef void (*Method)(); + Method m_method; + void _Execute() { (*m_method)(); } + public: + _SCallback(Method method) + : m_method(method) {} + _SCallback(_SCallback <> const& cb) + : m_method(cb.m_method) {} + }; +} + +/// --------- GENERIC CALLBACKS ---------- + +namespace MaNGOS +{ + class ICallback + { + public: + virtual void Execute() = 0; + virtual ~ICallback() {} + }; + + template < class CB > + class _ICallback : public CB, public ICallback + { + public: + _ICallback(CB const& cb) : CB(cb) {} + void Execute() { CB::_Execute(); } + }; + + template < class Class, typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void, typename ParamType4 = void > + class Callback : + public _ICallback< _Callback < Class, ParamType1, ParamType2, ParamType3, ParamType4 > > + { + private: + typedef _Callback < Class, ParamType1, ParamType2, ParamType3, ParamType4 > C4; + public: + Callback(Class *object, typename C4::Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3, ParamType4 param4) + : _ICallback< C4 >(C4(object, method, param1, param2, param3, param4)) {} + }; + + template < class Class, typename ParamType1, typename ParamType2, typename ParamType3 > + class Callback < Class, ParamType1, ParamType2, ParamType3 > : + public _ICallback< _Callback < Class, ParamType1, ParamType2, ParamType3 > > + { + private: + typedef _Callback < Class, ParamType1, ParamType2, ParamType3 > C3; + public: + Callback(Class *object, typename C3::Method method, ParamType1 param1, ParamType2 param2, ParamType3 param3) + : _ICallback< C3 >(C3(object, method, param1, param2, param3)) {} + }; + + template < class Class, typename ParamType1, typename ParamType2 > + class Callback < Class, ParamType1, ParamType2 > : + public _ICallback< _Callback < Class, ParamType1, ParamType2 > > + { + private: + typedef _Callback < Class, ParamType1, ParamType2 > C2; + public: + Callback(Class *object, typename C2::Method method, ParamType1 param1, ParamType2 param2) + : _ICallback< C2 >(C2(object, method, param1, param2)) {} + }; + + template < class Class, typename ParamType1 > + class Callback < Class, ParamType1 > : + public _ICallback< _Callback < Class, ParamType1 > > + { + private: + typedef _Callback < Class, ParamType1 > C1; + public: + Callback(Class *object, typename C1::Method method, ParamType1 param1) + : _ICallback< C1 >(C1(object, method, param1)) {} + }; + + template < class Class > + class Callback < Class > : public _ICallback< _Callback < Class > > + { + private: + typedef _Callback < Class > C0; + public: + Callback(Class *object, typename C0::Method method) + : _ICallback< C0 >(C0(object, method)) {} + }; +} + +/// ---------- QUERY CALLBACKS ----------- + +class QueryResult; + +namespace MaNGOS +{ + class IQueryCallback + { + public: + virtual void Execute() = 0; + virtual ~IQueryCallback() {} + virtual void SetResult(QueryResult* result) = 0; + virtual QueryResult* GetResult() = 0; + }; + + template < class CB > + class _IQueryCallback : public CB, public IQueryCallback + { + public: + _IQueryCallback(CB const& cb) : CB(cb) {} + void Execute() { CB::_Execute(); } + void SetResult(QueryResult* result) { CB::m_param1 = result; } + QueryResult* GetResult() { return CB::m_param1; } + }; + + template < class Class, typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void > + class QueryCallback : + public _IQueryCallback< _Callback < Class, QueryResult*, ParamType1, ParamType2, ParamType3 > > + { + private: + typedef _Callback < Class, QueryResult*, ParamType1, ParamType2, ParamType3 > QC3; + public: + QueryCallback(Class *object, typename QC3::Method method, QueryResult* result, ParamType1 param1, ParamType2 param2, ParamType3 param3) + : _IQueryCallback< QC3 >(QC3(object, method, result, param1, param2, param3)) {} + }; + + template < class Class, typename ParamType1, typename ParamType2 > + class QueryCallback < Class, ParamType1, ParamType2 > : + public _IQueryCallback< _Callback < Class, QueryResult*, ParamType1, ParamType2 > > + { + private: + typedef _Callback < Class, QueryResult*, ParamType1, ParamType2 > QC2; + public: + QueryCallback(Class *object, typename QC2::Method method, QueryResult* result, ParamType1 param1, ParamType2 param2) + : _IQueryCallback< QC2 >(QC2(object, method, result, param1, param2)) {} + }; + + template < class Class, typename ParamType1 > + class QueryCallback < Class, ParamType1 > : + public _IQueryCallback< _Callback < Class, QueryResult*, ParamType1 > > + { + private: + typedef _Callback < Class, QueryResult*, ParamType1 > QC1; + public: + QueryCallback(Class *object, typename QC1::Method method, QueryResult* result, ParamType1 param1) + : _IQueryCallback< QC1 >(QC1(object, method, result, param1)) {} + }; + + template < class Class > + class QueryCallback < Class > : public _IQueryCallback< _Callback < Class, QueryResult* > > + { + private: + typedef _Callback < Class, QueryResult* > QC0; + public: + QueryCallback(Class *object, typename QC0::Method method, QueryResult* result) + : _IQueryCallback< QC0 >(QC0(object, method, result)) {} + }; + + /// ---- Statics ---- + + template < typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void > + class SQueryCallback : + public _IQueryCallback< _SCallback < QueryResult*, ParamType1, ParamType2, ParamType3 > > + { + private: + typedef _SCallback < QueryResult*, ParamType1, ParamType2, ParamType3 > QC3; + public: + SQueryCallback(typename QC3::Method method, QueryResult* result, ParamType1 param1, ParamType2 param2, ParamType3 param3) + : _IQueryCallback< QC3 >(QC3(method, result, param1, param2, param3)) {} + }; + + template < typename ParamType1, typename ParamType2 > + class SQueryCallback < ParamType1, ParamType2 > : + public _IQueryCallback< _SCallback < QueryResult*, ParamType1, ParamType2 > > + { + private: + typedef _SCallback < QueryResult*, ParamType1, ParamType2 > QC2; + public: + SQueryCallback(typename QC2::Method method, QueryResult* result, ParamType1 param1, ParamType2 param2) + : _IQueryCallback< QC2 >(QC2(method, result, param1, param2)) {} + }; + + template < typename ParamType1 > + class SQueryCallback < ParamType1 > : + public _IQueryCallback< _SCallback < QueryResult*, ParamType1 > > + { + private: + typedef _SCallback < QueryResult*, ParamType1 > QC1; + public: + SQueryCallback(typename QC1::Method method, QueryResult* result, ParamType1 param1) + : _IQueryCallback< QC1 >(QC1(method, result, param1)) {} + }; + + template < > + class SQueryCallback < > : public _IQueryCallback< _SCallback < QueryResult* > > + { + private: + typedef _SCallback < QueryResult* > QC0; + public: + SQueryCallback(QC0::Method method, QueryResult* result) + : _IQueryCallback< QC0 >(QC0(method, result)) {} + }; +} + +#endif diff --git a/src/framework/Utilities/CountedReference/Reference.h b/src/framework/Utilities/CountedReference/Reference.h new file mode 100644 index 00000000000..581c6399a1b --- /dev/null +++ b/src/framework/Utilities/CountedReference/Reference.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_REFERENCE_H +#define MANGOS_REFERENCE_H + +/** + * Referencer + * Referencer is an object that holds a reference holder that hold a reference + * counted object. When an object's reference count drop to zero, it removes + * the object. This is a non intrusive mechanism and any object at any point + * in time can be referenced. When and object is reference counted, do not + * pass the object directly to other methods but rather, pass its + * reference around. Objects can be reference counted in both single threaded + * model and multi-threaded model + */ + +#include +#include "Platform/Define.h" +#include "Policies/ThreadingModel.h" +#include "ReferenceHolder.h" + +template +< +typename T, +class THREADING_MODEL = MaNGOS::SingleThreaded +> +class MANGOS_DLL_DECL Referencer +{ + typedef typename THREADING_MODEL::Lock Lock; + typedef ReferenceHolder ReferenceeHolder; + public: + + /// Constructs a referencer. + Referencer(T *ref = NULL); + + /// Copy constructor + Referencer(const Referencer &obj) : i_holder(NULL) { *this = obj; } + + /// Destructor + ~Referencer(); + + /// Referencee accessor + T* referencee(void) { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + const T* referencee(void) const { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + + //T& referencee(void){ return _referencee(); } + //const T& referencee(void) const { return const_cast(this)->_referencee(); } + operator T&(void) { return _referencee(); } + operator const T&(void) const { return *const_cast(this)->_referencee(); } + + /// cast operators + T* operator*() { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + T const * operator*() const { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + + /// overload operators + T* operator->() { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + const T * operator->() const { return (i_holder == NULL ? NULL : i_holder->i_referencee); } + + /// operator = + Referencer& operator=(const Referencer &obj); + Referencer& operator=(T *); + + /// returns true if i_referencee is null + bool isNull(void) const { return i_holder == NULL; } + + private: + + T& _referencee(void) + { + if( i_holder == NULL ) + throw std::runtime_error("Invalid access to null pointer"); + return *i_holder->i_referencee; + } + + void deReference(ReferenceeHolder *); + void addReference(ReferenceeHolder *); + + // private data + ReferenceeHolder *i_holder; +}; +#endif diff --git a/src/framework/Utilities/CountedReference/ReferenceHolder.h b/src/framework/Utilities/CountedReference/ReferenceHolder.h new file mode 100644 index 00000000000..5387b14107a --- /dev/null +++ b/src/framework/Utilities/CountedReference/ReferenceHolder.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_REFERENCEHOLDER_H +#define MANGOS_REFERENCEHOLDER_H + +/** ReferenceHolder holds the actualy referenced obejct as well the refence + count. The ReferenecHolder implements as a policy base object and + will decided by the Reference class to be consnsitent. + */ + +template +< +typename T, +class THREADING_MODEL +> +struct ReferenceHolder : public THREADING_MODEL +{ + explicit ReferenceHolder(T *ref) : i_referencee(ref), i_referenceCount(0) {} + T *i_referencee; + unsigned int i_referenceCount; + typedef typename THREADING_MODEL::Lock Lock; +}; +#endif diff --git a/src/framework/Utilities/CountedReference/ReferenceImpl.h b/src/framework/Utilities/CountedReference/ReferenceImpl.h new file mode 100644 index 00000000000..dffd05c52c4 --- /dev/null +++ b/src/framework/Utilities/CountedReference/ReferenceImpl.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_REFERENCEIMPL_H +#define MANGOS_REFERENCEIMPL_H + +#include "Reference.h" + +template +< +typename T, +class THREADING_MODEL +> +Referencer::Referencer(T *ref) +: i_holder(NULL) +{ + if( ref != NULL ) + { + i_holder = new ReferenceeHolder(ref); + ++i_holder->i_referenceCount; + } +} + +template +< +typename T, +class THREADING_MODEL +> +Referencer::~Referencer() +{ + if( i_holder != NULL ) + deReference(i_holder); + i_holder = NULL; +} + +template +< +typename T, +class THREADING_MODEL +> +Referencer& +Referencer::operator=(const Referencer &obj) +{ + if( i_holder != NULL ) + deReference(i_holder); + if( obj.i_holder != NULL ) + addReference(obj.i_holder); + i_holder = obj.i_holder; + return *this; +} + +template +< +typename T, +class THREADING_MODEL +> +Referencer& +Referencer::operator=(T *ref) +{ + if( i_holder != NULL ) + deReference(i_holder); + i_holder = NULL; + if( ref != NULL ) + { + i_holder = new ReferenceeHolder(ref); + ++i_holder->i_referenceCount; + } + + return *this; +} + +template +< +typename T, +class THREADING_MODEL +> +void +Referencer::deReference(ReferenceHolder *holder) +{ + assert( holder != NULL && holder->i_referenceCount > 0); + bool delete_object = false; + + { + // The guard is within the scope due to the guard + // must release earlier than expected. + Lock guard(*holder); + Guard(&guard); + + --holder->i_referenceCount; + if( holder->i_referenceCount == 0 ) + delete_object = true; + } + + if( delete_object ) + { + delete holder->i_referencee; + delete holder; + } +} + +template +< +typename T, +class THREADING_MODEL +> +void +Referencer::addReference(ReferenceHolder *holder) +{ + assert( i_holder != NULL ); + Lock guard(*holder); + Guard(&guard); + + ++holder->i_referenceCount; +} +#endif diff --git a/src/framework/Utilities/EventProcessor.cpp b/src/framework/Utilities/EventProcessor.cpp new file mode 100644 index 00000000000..55581ff7f8c --- /dev/null +++ b/src/framework/Utilities/EventProcessor.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "EventProcessor.h" + +EventProcessor::EventProcessor() +{ + m_time = 0; + m_aborting = false; +} + +EventProcessor::~EventProcessor() +{ + KillAllEvents(); +} + +void EventProcessor::Update(uint32 p_time) +{ + // update time + m_time += p_time; + + // main event loop + EventList::iterator i; + while (((i = m_events.begin()) != m_events.end()) && i->first <= m_time) + { + // get and remove event from queue + BasicEvent* Event = i->second; + m_events.erase(i); + + if (!Event->to_Abort) + { + if (Event->Execute(m_time, p_time)) + { + // completely destroy event if it is not re-added + delete Event; + } + } + else + { + Event->Abort(m_time); + delete Event; + } + } +} + +void EventProcessor::KillAllEvents() +{ + // prevent event insertions + m_aborting = true; + + // first, abort all existing events + for (EventList::iterator i = m_events.begin(); i != m_events.end(); ++i) + { + i->second->to_Abort = true; + i->second->Abort(m_time); + delete i->second; + } + + // clear event list + m_events.clear(); +} + +void EventProcessor::AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime) +{ + if (set_addtime) Event->m_addTime = m_time; + Event->m_execTime = e_time; + m_events.insert(std::pair(e_time, Event)); +} + +uint64 EventProcessor::CalculateTime(uint64 t_offset) +{ + return(m_time + t_offset); +} diff --git a/src/framework/Utilities/EventProcessor.h b/src/framework/Utilities/EventProcessor.h new file mode 100644 index 00000000000..d0a94ce08ae --- /dev/null +++ b/src/framework/Utilities/EventProcessor.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __EVENTPROCESSOR_H +#define __EVENTPROCESSOR_H + +#include "Platform/Define.h" + +#include + +// Note. All times are in milliseconds here. + +class BasicEvent +{ + public: + BasicEvent() { to_Abort = false; } + virtual ~BasicEvent() // override destructor to perform some actions on event removal + { + }; + + // this method executes when the event is triggered + // return false if event does not want to be deleted + // e_time is execution time, p_time is update interval + virtual bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) { return true; } + + virtual void Abort(uint64 /*e_time*/) {} // this method executes when the event is aborted + + bool to_Abort; // set by externals when the event is aborted, aborted events don't execute + // and get Abort call when deleted + + // these can be used for time offset control + uint64 m_addTime; // time when the event was added to queue, filled by event handler + uint64 m_execTime; // planned time of next execution, filled by event handler +}; + +typedef std::multimap EventList; + +class EventProcessor +{ + public: + EventProcessor(); + ~EventProcessor(); + + void Update(uint32 p_time); + void KillAllEvents(); + void AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime = true); + uint64 CalculateTime(uint64 t_offset); + protected: + uint64 m_time; + EventList m_events; + bool m_aborting; +}; +#endif diff --git a/src/framework/Utilities/HashMap.h b/src/framework/Utilities/HashMap.h new file mode 100644 index 00000000000..2f887bd1943 --- /dev/null +++ b/src/framework/Utilities/HashMap.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_HASHMAP_H +#define MANGOS_HASHMAP_H + +#include "Platform/CompilerDefs.h" +#include "Platform/Define.h" + +#if COMPILER == COMPILER_INTEL +#include +#elif COMPILER == COMPILER_GNU && __GNUC__ >= 3 +#include +#else +#include +#endif + +#ifdef _STLPORT_VERSION +#define HM_NAMESPACE std +using std::hash_map; +#elif COMPILER == COMPILER_MICROSOFT && _MSC_VER >= 1300 +#define HM_NAMESPACE stdext +using stdext::hash_map; +#elif COMPILER == COMPILER_INTEL +#define HM_NAMESPACE std +using std::hash_map; +#elif COMPILER == COMPILER_GNU && __GNUC__ >= 3 +#define HM_NAMESPACE __gnu_cxx +using __gnu_cxx::hash_map; + +namespace __gnu_cxx +{ + template<> struct hash + { + size_t operator()(const unsigned long long &__x) const { return (size_t)__x; } + }; + template struct hash + { + size_t operator()(T * const &__x) const { return (size_t)__x; } + }; + +}; + +#else +#define HM_NAMESPACE std +using std::hash_map; +#endif +#endif diff --git a/src/framework/Utilities/LinkedList.h b/src/framework/Utilities/LinkedList.h new file mode 100644 index 00000000000..3686c12c34c --- /dev/null +++ b/src/framework/Utilities/LinkedList.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINKEDLIST +#define _LINKEDLIST + +#include "Common.h" + +//============================================ +class LinkedListHead; + +class LinkedListElement +{ + private: + friend class LinkedListHead; + + LinkedListElement* iNext; + LinkedListElement* iPrev; + public: + LinkedListElement() { iNext = NULL; iPrev = NULL; } + ~LinkedListElement() { delink(); } + + bool hasNext() const { return(iNext->iNext != NULL); } + bool hasPrev() const { return(iPrev->iPrev != NULL); } + bool isInList() const { return(iNext != NULL && iPrev != NULL); } + + LinkedListElement * next() { return hasNext() ? iNext : NULL; } + LinkedListElement const* next() const { return hasNext() ? iNext : NULL; } + LinkedListElement * prev() { return hasPrev() ? iPrev : NULL; } + LinkedListElement const* prev() const { return hasPrev() ? iPrev : NULL; } + + void delink() + { + if(isInList()) + { + iNext->iPrev = iPrev; iPrev->iNext = iNext; iNext = NULL; iPrev = NULL; + } + } + + void insertBefore(LinkedListElement* pElem) + { + pElem->iNext = this; + pElem->iPrev = iPrev; + iPrev->iNext = pElem; + iPrev = pElem; + } + + void insertAfter(LinkedListElement* pElem) + { + pElem->iPrev = this; + pElem->iNext = iNext; + iNext->iPrev = pElem; + iNext = pElem; + } +}; + +//============================================ + +class LinkedListHead +{ + private: + LinkedListElement iFirst; + LinkedListElement iLast; + uint32 iSize; + public: + LinkedListHead() + { + // create empty list + + iFirst.iNext = &iLast; + iLast.iPrev = &iFirst; + iSize = 0; + } + + bool isEmpty() const { return(!iFirst.iNext->isInList()); } + + LinkedListElement * getFirst() { return(isEmpty() ? NULL : iFirst.iNext); } + LinkedListElement const* getFirst() const { return(isEmpty() ? NULL : iFirst.iNext); } + + LinkedListElement * getLast() { return(isEmpty() ? NULL : iLast.iPrev); } + LinkedListElement const* getLast() const { return(isEmpty() ? NULL : iLast.iPrev); } + + void insertFirst(LinkedListElement* pElem) + { + iFirst.insertAfter(pElem); + } + + void insertLast(LinkedListElement* pElem) + { + iLast.insertBefore(pElem); + } + + uint32 getSize() const + { + if(!iSize) + { + uint32 result = 0; + LinkedListElement const* e = getFirst(); + while(e) + { + ++result; + e = e->next(); + } + return result; + } + else + return iSize; + } + + void incSize() { ++iSize; } + void decSize() { --iSize; } + + template + class Iterator + { + public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef _Ty value_type; + typedef ptrdiff_t difference_type; + typedef ptrdiff_t distance_type; + typedef _Ty* pointer; + typedef _Ty& reference; + + Iterator() : _Ptr(0) + { // construct with null node pointer + } + + Iterator(pointer _Pnode) : _Ptr(_Pnode) + { // construct with node pointer _Pnode + } + + reference operator*() + { // return designated value + return *_Ptr; + } + + pointer operator->() + { // return pointer to class object + return _Ptr; + } + + Iterator& operator++() + { // preincrement + _Ptr = _Ptr->next(); + return (*this); + } + + Iterator operator++(int) + { // postincrement + iterator _Tmp = *this; + ++*this; + return (_Tmp); + } + + Iterator& operator--() + { // predecrement + _Ptr = _Ptr->prev(); + return (*this); + } + + Iterator operator--(int) + { // postdecrement + iterator _Tmp = *this; + --*this; + return (_Tmp); + } + + bool operator==(Iterator const &_Right) const + { // test for iterator equality + return (_Ptr == _Right._Ptr); + } + + bool operator!=(Iterator const &_Right) const + { // test for iterator inequality + return (!(*this == _Right)); + } + + bool operator==(pointer const &_Right) const + { // test for pointer equality + return (_Ptr != _Right); + } + + bool operator!=(pointer const &_Right) const + { // test for pointer equality + return (!(*this == _Right)); + } + + pointer _Mynode() + { // return node pointer + return (_Ptr); + } + + protected: + pointer _Ptr; // pointer to node + }; + + typedef Iterator iterator; +}; + +//============================================ +#endif diff --git a/src/framework/Utilities/LinkedReference/RefManager.h b/src/framework/Utilities/LinkedReference/RefManager.h new file mode 100644 index 00000000000..4e71ec7f2ed --- /dev/null +++ b/src/framework/Utilities/LinkedReference/RefManager.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _REFMANAGER_H +#define _REFMANAGER_H +//===================================================== + +#include "Utilities/LinkedList.h" +#include "Utilities/LinkedReference/Reference.h" + +template class RefManager : public LinkedListHead +{ + public: + typedef LinkedListHead::Iterator< Reference > iterator; + RefManager() { } + virtual ~RefManager() { clearReferences(); } + + Reference* getFirst() { return ((Reference*) LinkedListHead::getFirst()); } + Reference* getLast() { return ((Reference*) LinkedListHead::getLast()); } + + iterator begin() { return iterator(getFirst()); } + iterator end() { return iterator(NULL); } + iterator rbegin() { return iterator(getLast()); } + iterator rend() { return iterator(NULL); } + + void clearReferences() + { + LinkedListElement* ref; + while((ref = getFirst()) != NULL) + { + ((Reference*) ref)->invalidate(); + ref->delink(); // the delink might be already done by invalidate(), but doing it here again does not hurt and insures an empty list + } + } +}; + +//===================================================== +#endif diff --git a/src/framework/Utilities/LinkedReference/Reference.h b/src/framework/Utilities/LinkedReference/Reference.h new file mode 100644 index 00000000000..682bfd82162 --- /dev/null +++ b/src/framework/Utilities/LinkedReference/Reference.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _REFERENCE_H +#define _REFERENCE_H + +#include "Utilities/LinkedList.h" + +//===================================================== + +template class Reference : public LinkedListElement +{ + private: + TO* iRefTo; + FROM* iRefFrom; + protected: + // Tell our refTo (target) object that we have a link + virtual void targetObjectBuildLink() = 0; + + // Tell our refTo (taget) object, that the link is cut + virtual void targetObjectDestroyLink() = 0; + + // Tell our refFrom (source) object, that the link is cut (Target destroyed) + virtual void sourceObjectDestroyLink() = 0; + public: + Reference() { iRefTo = NULL; iRefFrom = NULL; } + virtual ~Reference() {} + + // Create new link + inline void link(TO* toObj, FROM* fromObj) + { + assert(fromObj); // fromObj MUST not be NULL + if(isValid()) + unlink(); + if(toObj != NULL) + { + iRefTo = toObj; + iRefFrom = fromObj; + targetObjectBuildLink(); + } + } + + // We don't need the reference anymore. Call comes from the refFrom object + // Tell our refTo object, that the link is cut + inline void unlink() { targetObjectDestroyLink(); delink(); iRefTo = NULL; iRefFrom = NULL; } + + // Link is invalid due to destruction of referenced target object. Call comes from the refTo object + // Tell our refFrom object, that the link is cut + inline void invalidate() // the iRefFrom MUST remain!! + { + sourceObjectDestroyLink(); delink(); iRefTo = NULL; + } + + inline bool isValid() const // Only check the iRefTo + { + return iRefTo != NULL; + } + + Reference* next() { return((Reference*)LinkedListElement::next()); } + Reference* prev() { return((Reference*)LinkedListElement::prev()); } + + inline TO* operator ->() const { return iRefTo; } + inline TO* getTarget() const { return iRefTo; } + + inline FROM* getSource() const { return iRefFrom; } +}; + +//===================================================== +#endif diff --git a/src/framework/Utilities/TypeList.h b/src/framework/Utilities/TypeList.h new file mode 100644 index 00000000000..074f5eb2483 --- /dev/null +++ b/src/framework/Utilities/TypeList.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_TYPELIST_H +#define MANGOS_TYPELIST_H + +/* + @struct TypeList + TypeList is the most simple but yet the most powerfull class of all. It holds + at compile time the different type of objects in a linked list. + */ + +class TypeNull; + +template +struct TypeList +{ + typedef HEAD Head; + typedef TAIL Tail; +}; + +// enough for now.. can be expand at any point in time as needed +#define TYPELIST_1(T1) TypeList +#define TYPELIST_2(T1, T2) TypeList +#define TYPELIST_3(T1, T2, T3) TypeList +#define TYPELIST_4(T1, T2, T3, T4) TypeList +#define TYPELIST_5(T1, T2, T3, T4, T5) TypeList +#endif diff --git a/src/game/AccountMgr.cpp b/src/game/AccountMgr.cpp new file mode 100644 index 00000000000..3b14db46dba --- /dev/null +++ b/src/game/AccountMgr.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "AccountMgr.h" +#include "Database/DatabaseEnv.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "Policies/SingletonImp.h" +#include "Util.h" + +#ifdef DO_POSTGRESQL +extern DatabasePostgre loginDatabase; +#else +extern DatabaseMysql loginDatabase; +#endif + +INSTANTIATE_SINGLETON_1(AccountMgr); + +AccountMgr::AccountMgr() +{} + +AccountMgr::~AccountMgr() +{} + +AccountOpResult AccountMgr::CreateAccount(std::string username, std::string password) +{ + if(utf8length(username) > MAX_ACCOUNT_STR) + return AOR_NAME_TOO_LONG; // username's too long + + normilizeString(username); + normilizeString(password); + + loginDatabase.escape_string(username); + loginDatabase.escape_string(password); + + QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE username = '%s'", username.c_str()); + if(result) + { + delete result; + return AOR_NAME_ALREDY_EXIST; // username does already exist + } + + if(!loginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s',SHA1(CONCAT('%s',':','%s')),NOW())", username.c_str(), username.c_str(), password.c_str())) + return AOR_DB_INTERNAL_ERROR; // unexpected error + loginDatabase.Execute("INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM account, realmlist WHERE account.id NOT IN (SELECT acctid FROM realmcharacters)"); + + return AOR_OK; // everything's fine +} + +AccountOpResult AccountMgr::DeleteAccount(uint32 accid) +{ + QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + if(!result) + return AOR_NAME_NOT_EXIST; // account doesn't exist + delete result; + + result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE account='%d'",accid); + if (result) + { + do + { + Field *fields = result->Fetch(); + uint32 guidlo = fields[0].GetUInt32(); + uint64 guid = MAKE_NEW_GUID(guidlo, 0, HIGHGUID_PLAYER); + + // kick if player currently + if(Player* p = objmgr.GetPlayer(guid)) + { + WorldSession* s = p->GetSession(); + s->KickPlayer(); // mark session to remove at next session list update + s->LogoutPlayer(false); // logout player without waiting next session list update + } + + Player::DeleteFromDB(guid, accid, false); // no need to update realm characters + } while (result->NextRow()); + + delete result; + } + + // table realm specific but common for all characters of account for realm + CharacterDatabase.PExecute("DELETE FROM character_tutorial WHERE account = '%u'",accid); + + loginDatabase.BeginTransaction(); + + bool res = + loginDatabase.PExecute("DELETE FROM account WHERE id='%d'", accid) && + loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid='%d'", accid); + + loginDatabase.CommitTransaction(); + + if(!res) + return AOR_DB_INTERNAL_ERROR; // unexpected error; + + return AOR_OK; +} + +AccountOpResult AccountMgr::ChangeUsername(uint32 accid, std::string new_uname, std::string new_passwd) +{ + QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + if(!result) + return AOR_NAME_NOT_EXIST; // account doesn't exist + delete result; + + if(utf8length(new_uname) > MAX_ACCOUNT_STR) + return AOR_NAME_TOO_LONG; + + if(utf8length(new_passwd) > MAX_ACCOUNT_STR) + return AOR_PASS_TOO_LONG; + + normilizeString(new_uname); + normilizeString(new_passwd); + + loginDatabase.escape_string(new_uname); + loginDatabase.escape_string(new_passwd); + if(!loginDatabase.PExecute("UPDATE account SET username='%s',sha_pass_hash=SHA1(CONCAT('%s',':','%s')) WHERE id='%d'", new_uname.c_str(), new_uname.c_str(), new_passwd.c_str(), accid)) + return AOR_DB_INTERNAL_ERROR; // unexpected error + + return AOR_OK; +} + +AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd) +{ + QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d'", accid); + if(!result) + return AOR_NAME_NOT_EXIST; // account doesn't exist + delete result; + + if (utf8length(new_passwd) > MAX_ACCOUNT_STR) + return AOR_PASS_TOO_LONG; + + normilizeString(new_passwd); + + loginDatabase.escape_string(new_passwd); + if(!loginDatabase.PExecute("UPDATE account SET sha_pass_hash=SHA1(CONCAT(username,':','%s')) WHERE id='%d'", new_passwd.c_str(), accid)) + return AOR_DB_INTERNAL_ERROR; // unexpected error + + return AOR_OK; +} + +uint32 AccountMgr::GetId(std::string username) +{ + loginDatabase.escape_string(username); + QueryResult *result = loginDatabase.PQuery("SELECT id FROM account WHERE username = '%s'", username.c_str()); + if(!result) + return 0; + else + { + uint32 id = (*result)[0].GetUInt32(); + delete result; + return id; + } +} + +bool AccountMgr::CheckPassword(uint32 accid, std::string passwd) +{ + normilizeString(passwd); + loginDatabase.escape_string(passwd); + + QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d' AND sha_pass_hash=SHA1(CONCAT(username,':','%s'))", accid, passwd.c_str()); + if (result) + { + delete result; + return true; + } + + return false; +} + +bool AccountMgr::normilizeString(std::string& utf8str) +{ + wchar_t wstr_buf[MAX_ACCOUNT_STR+1]; + + size_t wstr_len = MAX_ACCOUNT_STR; + if(!Utf8toWStr(utf8str,wstr_buf,wstr_len)) + return false; + + std::transform( &wstr_buf[0], wstr_buf+wstr_len, &wstr_buf[0], wcharToUpperOnlyLatin ); + + return WStrToUtf8(wstr_buf,wstr_len,utf8str); +} diff --git a/src/game/AccountMgr.h b/src/game/AccountMgr.h new file mode 100644 index 00000000000..ed04773a8d3 --- /dev/null +++ b/src/game/AccountMgr.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ACCMGR_H +#define _ACCMGR_H + +#include "Common.h" +#include "Policies/Singleton.h" +#include + +enum AccountOpResult +{ + AOR_OK, + AOR_NAME_TOO_LONG, + AOR_PASS_TOO_LONG, + AOR_NAME_ALREDY_EXIST, + AOR_NAME_NOT_EXIST, + AOR_DB_INTERNAL_ERROR +}; + +#define MAX_ACCOUNT_STR 16 + +class AccountMgr +{ + public: + AccountMgr(); + ~AccountMgr(); + + AccountOpResult CreateAccount(std::string username, std::string password); + AccountOpResult DeleteAccount(uint32 accid); + AccountOpResult ChangeUsername(uint32 accid, std::string new_uname, std::string new_passwd); + AccountOpResult ChangePassword(uint32 accid, std::string new_passwd); + bool CheckPassword(uint32 accid, std::string passwd); + + uint32 GetId(std::string username); + + static bool normilizeString(std::string& utf8str); +}; + +#define accmgr MaNGOS::Singleton::Instance() +#endif diff --git a/src/game/AddonHandler.cpp b/src/game/AddonHandler.cpp new file mode 100644 index 00000000000..a03ea0359ad --- /dev/null +++ b/src/game/AddonHandler.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "AddonHandler.h" +#include "Database/DatabaseEnv.h" +#include "Opcodes.h" +#include "Log.h" +#include "Policies/SingletonImp.h" +#include "zlib/zlib.h" + +INSTANTIATE_SINGLETON_1( AddonHandler ); + +AddonHandler::AddonHandler() +{ +} + +AddonHandler::~AddonHandler() +{ +} + +bool AddonHandler::BuildAddonPacket(WorldPacket *Source, WorldPacket *Target) +{ + ByteBuffer AddOnPacked; + uLongf AddonRealSize; + uint32 CurrentPosition; + uint32 TempValue; + + unsigned char tdata[256] = + { + 0xC3, 0x5B, 0x50, 0x84, 0xB9, 0x3E, 0x32, 0x42, 0x8C, 0xD0, 0xC7, 0x48, 0xFA, 0x0E, 0x5D, 0x54, + 0x5A, 0xA3, 0x0E, 0x14, 0xBA, 0x9E, 0x0D, 0xB9, 0x5D, 0x8B, 0xEE, 0xB6, 0x84, 0x93, 0x45, 0x75, + 0xFF, 0x31, 0xFE, 0x2F, 0x64, 0x3F, 0x3D, 0x6D, 0x07, 0xD9, 0x44, 0x9B, 0x40, 0x85, 0x59, 0x34, + 0x4E, 0x10, 0xE1, 0xE7, 0x43, 0x69, 0xEF, 0x7C, 0x16, 0xFC, 0xB4, 0xED, 0x1B, 0x95, 0x28, 0xA8, + 0x23, 0x76, 0x51, 0x31, 0x57, 0x30, 0x2B, 0x79, 0x08, 0x50, 0x10, 0x1C, 0x4A, 0x1A, 0x2C, 0xC8, + 0x8B, 0x8F, 0x05, 0x2D, 0x22, 0x3D, 0xDB, 0x5A, 0x24, 0x7A, 0x0F, 0x13, 0x50, 0x37, 0x8F, 0x5A, + 0xCC, 0x9E, 0x04, 0x44, 0x0E, 0x87, 0x01, 0xD4, 0xA3, 0x15, 0x94, 0x16, 0x34, 0xC6, 0xC2, 0xC3, + 0xFB, 0x49, 0xFE, 0xE1, 0xF9, 0xDA, 0x8C, 0x50, 0x3C, 0xBE, 0x2C, 0xBB, 0x57, 0xED, 0x46, 0xB9, + 0xAD, 0x8B, 0xC6, 0xDF, 0x0E, 0xD6, 0x0F, 0xBE, 0x80, 0xB3, 0x8B, 0x1E, 0x77, 0xCF, 0xAD, 0x22, + 0xCF, 0xB7, 0x4B, 0xCF, 0xFB, 0xF0, 0x6B, 0x11, 0x45, 0x2D, 0x7A, 0x81, 0x18, 0xF2, 0x92, 0x7E, + 0x98, 0x56, 0x5D, 0x5E, 0x69, 0x72, 0x0A, 0x0D, 0x03, 0x0A, 0x85, 0xA2, 0x85, 0x9C, 0xCB, 0xFB, + 0x56, 0x6E, 0x8F, 0x44, 0xBB, 0x8F, 0x02, 0x22, 0x68, 0x63, 0x97, 0xBC, 0x85, 0xBA, 0xA8, 0xF7, + 0xB5, 0x40, 0x68, 0x3C, 0x77, 0x86, 0x6F, 0x4B, 0xD7, 0x88, 0xCA, 0x8A, 0xD7, 0xCE, 0x36, 0xF0, + 0x45, 0x6E, 0xD5, 0x64, 0x79, 0x0F, 0x17, 0xFC, 0x64, 0xDD, 0x10, 0x6F, 0xF3, 0xF5, 0xE0, 0xA6, + 0xC3, 0xFB, 0x1B, 0x8C, 0x29, 0xEF, 0x8E, 0xE5, 0x34, 0xCB, 0xD1, 0x2A, 0xCE, 0x79, 0xC3, 0x9A, + 0x0D, 0x36, 0xEA, 0x01, 0xE0, 0xAA, 0x91, 0x20, 0x54, 0xF0, 0x72, 0xD8, 0x1E, 0xC7, 0x89, 0xD2 + }; + + // broken addon packet, can't be received from real client + if (Source->rpos() + 4 > Source->size()) + return false; + + *Source >> TempValue; //get real size of the packed structure + + // empty addon packet, nothing process, can't be received from real client + if(!TempValue) + return false; + + AddonRealSize = TempValue; //temp value because ZLIB only excepts uLongf + + CurrentPosition = Source->rpos(); //get the position of the pointer in the structure + + AddOnPacked.resize(AddonRealSize); //resize target for zlib action + + if (!uncompress(const_cast(AddOnPacked.contents()), &AddonRealSize, const_cast((*Source).contents() + CurrentPosition), (*Source).size() - CurrentPosition)!= Z_OK) + { + Target->Initialize(SMSG_ADDON_INFO); + + while(AddOnPacked.rpos() < AddOnPacked.size()) + { + std::string AddonNames; + uint8 unk6; + uint32 crc, unk7; + + // check next addon data format correctness + if(AddOnPacked.rpos()+1+4+4+1 > AddOnPacked.size()) + return false; + + AddOnPacked >> AddonNames; + + // recheck next addon data format correctness + if(AddOnPacked.rpos()+4+4+1 > AddOnPacked.size()) + return false; + + AddOnPacked >> crc >> unk7 >> unk6; + + //sLog.outDebug("ADDON: Name:%s CRC:%x Unknown1 :%x Unknown2 :%x", AddonNames.c_str(), crc, unk7, unk6); + + *Target << (uint8)2; + + uint8 unk1 = 1; + *Target << (uint8)unk1; + if (unk1) + { + uint8 unk2 = crc != 0x1c776d01LL; //If addon is Standard addon CRC + *Target << (uint8)unk2; + if (unk2) + Target->append(tdata, sizeof(tdata)); + + *Target << (uint32)0; + } + + uint8 unk3 = 0; + *Target << (uint8)unk3; + if (unk3) + { + // String, 256 + } + } + } + else + { + sLog.outError("Addon packet uncompress error :("); + return false; + } + return true; +} + +/* Code use in 1.10.2 when client not ignore ban state sended for addons. Saved for reference if client switch to use server ban state information +void AddonHandler::BuildAddonPacket(WorldPacket* Source, WorldPacket* Target, uint32 Packetoffset) +{ + ByteBuffer AddOnPacked; + uLongf AddonRealSize; + uint32 CurrentPosition; + uint32 TempValue; + + *Source >> TempValue; //get real size of the packed structure + + AddonRealSize = TempValue; //temp value becouse ZLIB only excepts uLongf + + CurrentPosition = Source->rpos(); //get the position of the pointer in the structure + + AddOnPacked.resize(AddonRealSize); //resize target for zlib action + + if (!uncompress((uint8*)AddOnPacked.contents(), &AddonRealSize, (uint8*)(*Source).contents() + CurrentPosition, (*Source).size() - CurrentPosition)!= Z_OK) + { + bool* AddonAllowed = new bool; //handle addon check and enable-ing + + uint32 Unknown1; + uint8 Unknown0; + + AddOnPacked >> Unknown0; + AddOnPacked >> Unknown1; + + Target->Initialize(SMSG_ADDON_INFO); + + uint32 i = 5; //offset for addon extraction + while(i != AddOnPacked.size()) + { + std::string AddonNames; + AddOns* Addonstr = new AddOns; + uint8 unk6; + uint64 CRCCHECK; + AddOnPacked >> AddonNames >> CRCCHECK >> unk6; + + //sLog.outDebug("ADDON: Name:%s CRC:%x Unknown:%x",AddonNames.c_str(), CRCCHECK,unk6); + + Addonstr->Name = AddonNames; + Addonstr->CRC = CRCCHECK; + + //if not allowed but unknown added to list + if (GetAddonStatus(Addonstr, AddonAllowed)) // If addon is new + { + Addonstr->Enabled = m_Addon_Default; // by default new addons are set from Config file + *AddonAllowed = m_Addon_Default; // Set addon allowed on default value + _AddAddon(Addonstr); + sLog.outDetail("Found new Addon, Name:%s CRC:%x Unknown:%x",AddonNames.c_str(), CRCCHECK, unk6); + } + + if (CRCCHECK == 0x4C1C776D01LL) //If addon is Standard addon CRC + { + //value's standard Addons + *Target << uint8(0) << uint8(2) << uint8(1) << uint8(0) << uint32(0); + } + else if (*AddonAllowed) //if addon is Custom addons + //value's enable addon + *Target << uint8(0x00) << uint8(0x01) << uint8(0x00) << uint8(0x01); + else + //value's disable addom + *Target << uint8(0x00) << uint8(0x0) << uint8(0x00) << uint8(0x0); + + i += AddonNames.size() + 10; + } + *Target << uint8(0x0); + + //delete mem allocation + delete AddonAllowed; + } + else + { + //handle uncompress error + } +} +*/ diff --git a/src/game/AddonHandler.h b/src/game/AddonHandler.h new file mode 100644 index 00000000000..2e2db5011b2 --- /dev/null +++ b/src/game/AddonHandler.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ADDONHANDLER_H +#define __ADDONHANDLER_H + +#include "Common.h" +#include "Policies/Singleton.h" +#include "WorldPacket.h" +#include "Config/ConfigEnv.h" + +class AddonHandler +{ + public: + /* Construction */ + AddonHandler(); + ~AddonHandler(); + //built addon packet + bool BuildAddonPacket(WorldPacket* Source, WorldPacket* Target); +}; +#define sAddOnHandler MaNGOS::Singleton::Instance() +#endif diff --git a/src/game/AggressorAI.cpp b/src/game/AggressorAI.cpp new file mode 100644 index 00000000000..494acbb9a51 --- /dev/null +++ b/src/game/AggressorAI.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "AggressorAI.h" +#include "Errors.h" +#include "Creature.h" +#include "Player.h" +#include "ObjectAccessor.h" +#include "VMapFactory.h" +#include "World.h" + +#include + +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) +{ + if( i_creature.GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE ) + return; + if( !i_creature.getVictim() && !i_creature.hasUnitState(UNIT_STAT_STUNDED) && 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/AggressorAI.h b/src/game/AggressorAI.h new file mode 100644 index 00000000000..b90b12182b2 --- /dev/null +++ b/src/game/AggressorAI.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_AGGRESSORAI_H +#define MANGOS_AGGRESSORAI_H + +#include "CreatureAI.h" +#include "Timer.h" + +class Creature; + +class MANGOS_DLL_DECL AggressorAI : public CreatureAI +{ + enum AggressorState + { + STATE_NORMAL = 1, + STATE_LOOK_AT_VICTIM = 2 + }; + + public: + + AggressorAI(Creature &c); + + void MoveInLineOfSight(Unit *); + void AttackStart(Unit *); + void EnterEvadeMode(); + bool IsVisible(Unit *) const; + + void UpdateAI(const uint32); + static int Permissible(const Creature *); + + private: + Creature &i_creature; + uint64 i_victimGuid; + AggressorState i_state; + TimeTracker i_tracker; +}; +#endif diff --git a/src/game/AnimalRandomMovementGenerator.h b/src/game/AnimalRandomMovementGenerator.h new file mode 100644 index 00000000000..a40eb65eb07 --- /dev/null +++ b/src/game/AnimalRandomMovementGenerator.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_ANIMAL_RANDOMMOVEMENTGENERATOR_H +#define MANGOS_ANIMAL_RANDOMMOVEMENTGENERATOR_H + +/** AnimalRandomMovementGenerator follows the research on + * quantifying scale-dependant effects of animal movement + * with simple per-location models (R.H. Gardner, R.V. O'Neil, + * M.G: Turner and V.H. Dale). It is specifically used on + * animal creatures. + */ +#endif diff --git a/src/game/ArenaTeam.cpp b/src/game/ArenaTeam.cpp new file mode 100644 index 00000000000..578340a4a8d --- /dev/null +++ b/src/game/ArenaTeam.cpp @@ -0,0 +1,517 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "WorldPacket.h" +#include "ObjectMgr.h" +#include "ArenaTeam.h" + +ArenaTeam::ArenaTeam() +{ + Id = 0; + Type = 0; + Name = ""; + CaptainGuid = 0; + BackgroundColor = 0; // background + EmblemStyle = 0; // icon + EmblemColor = 0; // icon color + BorderStyle = 0; // border + BorderColor = 0; // border color + stats.games = 0; + stats.played = 0; + stats.rank = 0; + stats.rating = 1500; + stats.wins = 0; + stats.wins2 = 0; +} + +ArenaTeam::~ArenaTeam() +{ + +} + +bool ArenaTeam::create(uint64 captainGuid, uint32 type, std::string ArenaTeamName) +{ + if(!objmgr.GetPlayer(captainGuid)) // player not exist + return false; + if(objmgr.GetArenaTeamByName(ArenaTeamName)) // arena team with this name already exist + return false; + + sLog.outDebug("GUILD: creating arena team %s to leader: %u", ArenaTeamName.c_str(), GUID_LOPART(CaptainGuid)); + + CaptainGuid = captainGuid; + Name = ArenaTeamName; + Type = type; + + QueryResult *result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team"); + if( result ) + { + Id = (*result)[0].GetUInt32()+1; + delete result; + } + else Id = 1; + + // ArenaTeamName already assigned to ArenaTeam::name, use it to encode string for DB + CharacterDatabase.escape_string(ArenaTeamName); + + CharacterDatabase.BeginTransaction(); + // CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid='%u'", Id); - MAX(arenateam)+1 not exist + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid='%u'", Id); + CharacterDatabase.PExecute("INSERT INTO arena_team (arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor) " + "VALUES('%u','%s','%u','%u','%u','%u','%u','%u','%u')", + Id, ArenaTeamName.c_str(), GUID_LOPART(CaptainGuid), Type, BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor); + CharacterDatabase.PExecute("INSERT INTO arena_team_stats (arenateamid, rating, games, wins, played, wins2, rank) VALUES " + "('%u', '%u', '%u', '%u', '%u', '%u', '%u')", Id,stats.rating,stats.games,stats.wins,stats.played,stats.wins2,stats.rank); + + CharacterDatabase.CommitTransaction(); + + AddMember(CaptainGuid); + return true; +} + +bool ArenaTeam::AddMember(uint64 PlayerGuid) +{ + std::string plName; + uint8 plClass; + + if(GetMembersSize() >= GetType() * 2) + { + // arena team is full (can't have more than type * 2 players!) + // return false + return false; + } + + if(!objmgr.GetPlayerNameByGUID(PlayerGuid, plName)) // player doesnt exist + return false; + // player already in arenateam of that size + if(Player::GetArenaTeamIdFromDB(PlayerGuid, GetType()) != 0) + { + sLog.outError("Arena::AddMember() : player already in this sized team"); + return false; + } + + // remove all player signs from another petitions + // this will be prevent attempt joining player to many arenateams and corrupt arena team data integrity + Player::RemovePetitionsAndSigns(PlayerGuid, GetType()); + + Player *pl = objmgr.GetPlayer(PlayerGuid); + if(pl) + { + plClass = (uint8)pl->getClass(); + } + else + { + QueryResult *result = CharacterDatabase.PQuery("SELECT class FROM characters WHERE guid='%u'", GUID_LOPART(PlayerGuid)); + if(!result) + return false; + plClass = (*result)[0].GetUInt8(); + delete result; + } + + ArenaTeamMember newmember; + newmember.name = plName; + newmember.guid = PlayerGuid; + newmember.Class = plClass; + newmember.played_season = 0; + newmember.played_week = 0; + newmember.wons_season = 0; + newmember.wons_week = 0; + members.push_back(newmember); + + CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid,guid) VALUES ('%u', '%u')", Id, GUID_LOPART(newmember.guid)); + + if(pl) + { + pl->SetInArenaTeam(Id, GetSlot()); + pl->SetArenaTeamIdInvited(0); + } + else + { + Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6), Id, PlayerGuid); + } + + // hide promote/remove buttons + if(CaptainGuid != PlayerGuid) + { + if(pl) + pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1); + else + Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1, PlayerGuid); + } + return true; +} + +bool ArenaTeam::LoadArenaTeamFromDB(uint32 ArenaTeamId) +{ + LoadStatsFromDB(ArenaTeamId); + LoadMembersFromDB(ArenaTeamId); + + // 0 1 2 3 4 5 6 7 8 + QueryResult *result = CharacterDatabase.PQuery("SELECT arenateamid,name,captainguid,type,BackgroundColor,EmblemStyle,EmblemColor,BorderStyle,BorderColor FROM arena_team WHERE arenateamid = '%u'", ArenaTeamId); + + if(!result) + return false; + + Field *fields = result->Fetch(); + + Id = fields[0].GetUInt32(); + Name = fields[1].GetCppString(); + CaptainGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER); + Type = fields[3].GetUInt32(); + BackgroundColor = fields[4].GetUInt32(); + EmblemStyle = fields[5].GetUInt32(); + EmblemColor = fields[6].GetUInt32(); + BorderStyle = fields[7].GetUInt32(); + BorderColor = fields[8].GetUInt32(); + + delete result; + + return true; +} + +void ArenaTeam::LoadStatsFromDB(uint32 ArenaTeamId) +{ + // 0 1 2 3 4 5 + QueryResult *result = CharacterDatabase.PQuery("SELECT rating,games,wins,played,wins2,rank FROM arena_team_stats WHERE arenateamid = '%u'", ArenaTeamId); + + if(!result) + return; + + Field *fields = result->Fetch(); + + stats.rating = fields[0].GetUInt32(); + stats.games = fields[1].GetUInt32(); + stats.wins = fields[2].GetUInt32(); + stats.played = fields[3].GetUInt32(); + stats.wins2 = fields[4].GetUInt32(); + stats.rank = fields[5].GetUInt32(); + + delete result; +} + +void ArenaTeam::LoadMembersFromDB(uint32 ArenaTeamId) +{ + Field *fields; + + QueryResult *result = CharacterDatabase.PQuery("SELECT guid,played_week,wons_week,played_season,wons_season FROM arena_team_member WHERE arenateamid = '%u'", ArenaTeamId); + if(!result) + return; + + do + { + fields = result->Fetch(); + ArenaTeamMember newmember; + newmember.guid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); + LoadPlayerStats(&newmember); + newmember.played_week = fields[1].GetUInt32(); + newmember.wons_week = fields[2].GetUInt32(); + newmember.played_season = fields[3].GetUInt32(); + newmember.wons_season = fields[4].GetUInt32(); + members.push_back(newmember); + }while( result->NextRow() ); + delete result; +} + +void ArenaTeam::LoadPlayerStats(ArenaTeamMember *member) +{ + Field *fields; + + QueryResult *result = CharacterDatabase.PQuery("SELECT name,class FROM characters WHERE guid = '%u'", GUID_LOPART(member->guid)); + if(!result) + return; + fields = result->Fetch(); + member->name = fields[0].GetCppString(); + member->Class = fields[1].GetUInt8(); + + delete result; +} + +void ArenaTeam::SetCaptain(uint64 guid) +{ + // disable remove/promote buttons + Player *oldcaptain = objmgr.GetPlayer(GetCaptain()); + if(oldcaptain) + oldcaptain->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1); + else + Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 1, GetCaptain()); + + // set new captain + CaptainGuid = guid; + + // update database + CharacterDatabase.PExecute("UPDATE arena_team SET captainguid = '%u' WHERE arenateamid = '%u'", GUID_LOPART(guid), Id); + + // enable remove/promote buttons + Player *newcaptain = objmgr.GetPlayer(guid); + if(newcaptain) + newcaptain->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 0); + else + Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 + (GetSlot() * 6), 0, guid); +} + +void ArenaTeam::DelMember(uint64 guid) +{ + MemberList::iterator itr; + for (itr = members.begin(); itr != members.end(); itr++) + { + if (itr->guid == guid) + { + members.erase(itr); + break; + } + } + + Player *player = objmgr.GetPlayer(guid); + if(player) + { + player->SetInArenaTeam(0, GetSlot()); + player->GetSession()->SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, GetName(), "", 0); + } + else + { + Player::SetUInt32ValueInDB(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6), 0, guid); + } + + CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE guid = '%u'", GUID_LOPART(guid)); +} + +void ArenaTeam::Disband(WorldSession *session) +{ + // event + WorldPacket data; + session->BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_DISBANDED_S, 2, session->GetPlayerName(), GetName(), ""); + BroadcastPacket(&data); + + uint32 count = members.size(); + uint64 *memberGuids = new uint64[count]; + + MemberList::iterator itr; + uint32 i=0; + for(itr = members.begin(); itr != members.end(); itr++) + { + memberGuids[i] = itr->guid; + ++i; + } + + for(uint32 j = 0; j < count; j++) + DelMember(memberGuids[j]); + delete[] memberGuids; + + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", Id); + CharacterDatabase.PExecute("DELETE FROM arena_team_stats WHERE arenateamid = '%u'", Id); + CharacterDatabase.CommitTransaction(); + objmgr.RemoveArenaTeam(this); +} + +void ArenaTeam::Roster(WorldSession *session) +{ + Player *pl = NULL; + + WorldPacket data(SMSG_ARENA_TEAM_ROSTER, 100); + data << uint32(GetId()); // arena team id + data << uint32(GetMembersSize()); // members count + data << uint32(GetType()); // arena team type? + + for (MemberList::iterator itr = members.begin(); itr != members.end(); ++itr) + { + pl = objmgr.GetPlayer(itr->guid); + if(pl) + { + data << uint64(pl->GetGUID()); // guid + data << uint8(1); // online flag + data << pl->GetName(); // member name + data << uint32(itr->guid == GetCaptain() ? 0 : 1);// unknown + data << uint8(pl->getLevel()); // unknown, probably level + data << uint8(pl->getClass()); // class + data << uint32(itr->played_week); // played this week + data << uint32(itr->wons_week); // wins this week + data << uint32(itr->played_season); // played this season + data << uint32(itr->wons_season); // wins this season + data << uint32(0); // personal rating? + } + else + { + data << uint64(itr->guid); // guid + data << uint8(0); // online flag + data << itr->name; // member name + data << uint32(itr->guid == GetCaptain() ? 0 : 1);// unknown + data << uint8(0); // unknown, level? + data << uint8(itr->Class); // class + data << uint32(itr->played_week); // played this week + data << uint32(itr->wons_week); // wins this week + data << uint32(itr->played_season); // played this season + data << uint32(itr->wons_season); // wins this season + data << uint32(0); // personal rating? + } + } + session->SendPacket(&data); + sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_ROSTER"); +} + +void ArenaTeam::Query(WorldSession *session) +{ + WorldPacket data(SMSG_ARENA_TEAM_QUERY_RESPONSE, 4*7+GetName().size()+1); + data << uint32(GetId()); // team id + data << GetName(); // team name + data << uint32(GetType()); // arena team type (2=2x2, 3=3x3 or 5=5x5) + data << uint32(BackgroundColor); // background color + data << uint32(EmblemStyle); // emblem style + data << uint32(EmblemColor); // emblem color + data << uint32(BorderStyle); // border style + data << uint32(BorderColor); // border color + session->SendPacket(&data); + sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_QUERY_RESPONSE"); +} + +void ArenaTeam::Stats(WorldSession *session) +{ + WorldPacket data(SMSG_ARENA_TEAM_STATS, 4*7); + data << uint32(GetId()); // arena team id + data << uint32(stats.rating); // rating + data << uint32(stats.games); // games + data << uint32(stats.wins); // wins + data << uint32(stats.played); // played + data << uint32(stats.wins2); // wins(again o_O) + data << uint32(stats.rank); // rank + session->SendPacket(&data); +} + +void ArenaTeam::InspectStats(WorldSession *session, uint64 guid) +{ + WorldPacket data(MSG_INSPECT_ARENA_TEAMS, 8+1+4*6); + data << uint64(guid); // player guid + data << uint8(GetSlot()); // slot (0...2) + data << uint32(GetId()); // arena team id + data << uint32(stats.rating); // rating + data << uint32(stats.games); // games + data << uint32(stats.wins); // wins + data << uint32(stats.played); // played (count of all games, that played...) + data << uint32(0); // 2.3.3 personal rating? + session->SendPacket(&data); +} + +void ArenaTeam::SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor) +{ + BackgroundColor = backgroundColor; + EmblemStyle = emblemStyle; + EmblemColor = emblemColor; + BorderStyle = borderStyle; + BorderColor = borderColor; + + CharacterDatabase.PExecute("UPDATE arena_team SET BackgroundColor='%u', EmblemStyle='%u', EmblemColor='%u', BorderStyle='%u', BorderColor='%u' WHERE arenateamid='%u'", BackgroundColor, EmblemStyle, EmblemColor, BorderStyle, BorderColor, Id); +} + +void ArenaTeam::SetStats(uint32 stat_type, uint32 value) +{ + switch(stat_type) + { + case STAT_TYPE_RATING: + stats.rating = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET rating = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + case STAT_TYPE_GAMES: + stats.games = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET games = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + case STAT_TYPE_WINS: + stats.wins = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + case STAT_TYPE_PLAYED: + stats.played = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET played = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + case STAT_TYPE_WINS2: + stats.wins2 = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET wins2 = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + case STAT_TYPE_RANK: + stats.rank = value; + CharacterDatabase.PExecute("UPDATE arena_team_stats SET rank = '%u' WHERE arenateamid = '%u'", value, GetId()); + break; + default: + sLog.outDebug("unknown stat type in ArenaTeam::SetStats() %u", stat_type); + break; + } +} + +uint8 ArenaTeam::GetSlot() const +{ + uint8 slot = GetSlotByType(GetType()); + if(slot >= MAX_ARENA_SLOT) + { + sLog.outError("Unknown arena team type %u for arena team %u", uint32(GetType()), GetId()); + return 0; // better return existed slot to prevent untelated data curruption + } + + return slot; +} + +void ArenaTeam::BroadcastPacket(WorldPacket *packet) +{ + for (MemberList::iterator itr = members.begin(); itr != members.end(); itr++) + { + Player *player = objmgr.GetPlayer(itr->guid); + if(player) + player->GetSession()->SendPacket(packet); + } +} + +uint8 ArenaTeam::GetSlotByType( uint32 type ) +{ + switch(type) + { + case ARENA_TEAM_2v2: return 0; + case ARENA_TEAM_3v3: return 1; + case ARENA_TEAM_5v5: return 2; + default: + break; + } + return 0xFF; +} + +bool ArenaTeam::HaveMember( uint64 guid ) const +{ + for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr) + if(itr->guid==guid) + return true; + + return false; +} + +/* +arenateam fields (id from 2.3.3 client): +1414 - arena team id 2v2 +1415 - 0=captain, 1=member +1416 - played this season +1417 - played this week +1418 - unk +1419 - unk +1420 - arena team id 3v3 +1421 - 0=captain, 1=member +1422 - played this season +1423 - played this week +1424 - unk +1425 - unk +1426 - arena team id 5v5 +1427 - 0=captain, 1=member +1428 - played this season +1429 - played this week +1430 - unk +1431 - unk +*/ diff --git a/src/game/ArenaTeam.h b/src/game/ArenaTeam.h new file mode 100644 index 00000000000..9b4ef67ead1 --- /dev/null +++ b/src/game/ArenaTeam.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_ARENATEAM_H +#define MANGOSSERVER_ARENATEAM_H + +enum ArenaTeamCommandTypes +{ + ERR_ARENA_TEAM_CREATE_S = 0x00, + ERR_ARENA_TEAM_INVITE_SS = 0x01, + //ERR_ARENA_TEAM_QUIT_S = 0x02, + ERR_ARENA_TEAM_QUIT_S = 0x03, + ERR_ARENA_TEAM_FOUNDER_S = 0x0C // need check, probably wrong... +}; + +enum ArenaTeamCommandErrors +{ + //ARENA_TEAM_PLAYER_NO_MORE_IN_ARENA_TEAM = 0x00, + ERR_ARENA_TEAM_INTERNAL = 0x01, + ERR_ALREADY_IN_ARENA_TEAM = 0x02, + ERR_ALREADY_IN_ARENA_TEAM_S = 0x03, + ERR_INVITED_TO_ARENA_TEAM = 0x04, + ERR_ALREADY_INVITED_TO_ARENA_TEAM_S = 0x05, + ERR_ARENA_TEAM_NAME_INVALID = 0x06, + ERR_ARENA_TEAM_NAME_EXISTS_S = 0x07, + ERR_ARENA_TEAM_LEADER_LEAVE_S = 0x08, + ERR_ARENA_TEAM_PERMISSIONS = 0x08, + ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM = 0x09, + ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS = 0x0A, + ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S = 0x0B, + ERR_ARENA_TEAM_NOT_ALLIED = 0x0C +}; + +enum ArenaTeamEvents +{ + ERR_ARENA_TEAM_JOIN_SS = 3, // player name + arena team name + ERR_ARENA_TEAM_LEAVE_SS = 4, // player name + arena team name + ERR_ARENA_TEAM_REMOVE_SSS = 5, // player name + arena team name + captain name + ERR_ARENA_TEAM_LEADER_IS_SS = 6, // player name + arena team name + ERR_ARENA_TEAM_LEADER_CHANGED_SSS = 7, // old captain + new captain + arena team name + ERR_ARENA_TEAM_DISBANDED_S = 8 // captain name + arena team name +}; + +/* +need info how to send these ones: +ERR_ARENA_TEAM_YOU_JOIN_S - client show it automatically when accept invite +ERR_ARENA_TEAM_TARGET_TOO_LOW_S +ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S +ERR_ARENA_TEAM_LEVEL_TOO_LOW_I +*/ + +enum ArenaTeamStatTypes +{ + STAT_TYPE_RATING = 0, + STAT_TYPE_GAMES = 1, + STAT_TYPE_WINS = 2, + STAT_TYPE_PLAYED = 3, + STAT_TYPE_WINS2 = 4, + STAT_TYPE_RANK = 5 +}; + +enum ArenaTeamTypes +{ + ARENA_TEAM_2v2 = 2, + ARENA_TEAM_3v3 = 3, + ARENA_TEAM_5v5 = 5 +}; + +struct ArenaTeamMember +{ + uint64 guid; + std::string name; + //uint32 unk2; + //uint8 unk1; + uint8 Class; + uint32 played_week; + uint32 wons_week; + uint32 played_season; + uint32 wons_season; +}; + +struct ArenaTeamStats +{ + uint32 rating; + uint32 games; + uint32 wins; + uint32 played; + uint32 wins2; + uint32 rank; +}; + +#define MAX_ARENA_SLOT 3 // 0..2 slots + +class ArenaTeam +{ + public: + ArenaTeam(); + ~ArenaTeam(); + + bool create(uint64 CaptainGuid, uint32 type, std::string ArenaTeamName); + void Disband(WorldSession *session); + + typedef std::list MemberList; + + uint32 GetId() const { return Id; } + uint32 GetType() const { return Type; } + uint8 GetSlot() const; + static uint8 GetSlotByType(uint32 type); + const uint64& GetCaptain() const { return CaptainGuid; } + std::string GetName() const { return Name; } + ArenaTeamStats GetStats() const { return stats; } + void SetStats(uint32 stat_type, uint32 value); + uint32 GetRating() const { return stats.rating; } + + uint32 GetEmblemStyle() const { return EmblemStyle; } + uint32 GetEmblemColor() const { return EmblemColor; } + uint32 GetBorderStyle() const { return BorderStyle; } + uint32 GetBorderColor() const { return BorderColor; } + uint32 GetBackgroundColor() const { return BackgroundColor; } + + void SetCaptain(uint64 guid); + bool AddMember(uint64 PlayerGuid); + void DelMember(uint64 guid); + + void SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor); + + uint32 GetMembersSize() const { return members.size(); } + MemberList::iterator membersbegin(){ return members.begin(); } + MemberList::iterator membersEnd(){ return members.end(); } + bool HaveMember(uint64 guid) const; + + bool LoadArenaTeamFromDB(uint32 ArenaTeamId); + void LoadMembersFromDB(uint32 ArenaTeamId); + void LoadStatsFromDB(uint32 ArenaTeamId); + void LoadPlayerStats(ArenaTeamMember* member); + + void BroadcastPacket(WorldPacket *packet); + + void Roster(WorldSession *session); + void Query(WorldSession *session); + void Stats(WorldSession *session); + void InspectStats(WorldSession *session, uint64 guid); + + protected: + + uint32 Id; + uint32 Type; + std::string Name; + uint64 CaptainGuid; + + uint32 BackgroundColor; // ARGB format + uint32 EmblemStyle; // icon id + uint32 EmblemColor; // ARGB format + uint32 BorderStyle; // border image id + uint32 BorderColor; // ARGB format + + MemberList members; + ArenaTeamStats stats; +}; +#endif diff --git a/src/game/ArenaTeamHandler.cpp b/src/game/ArenaTeamHandler.cpp new file mode 100644 index 00000000000..33786e93532 --- /dev/null +++ b/src/game/ArenaTeamHandler.cpp @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "WorldSession.h" +#include "WorldPacket.h" +#include "Log.h" +#include "Database/DatabaseEnv.h" +#include "Player.h" +#include "ObjectMgr.h" +#include "ArenaTeam.h" +#include "World.h" +#include "SocialMgr.h" + +void WorldSession::HandleInspectArenaStatsOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("MSG_INSPECT_ARENA_TEAMS"); + //recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 8); + + uint64 guid; + recv_data >> guid; + sLog.outDebug("Inspect Arena stats " I64FMTD, guid); + + if(Player *plr = objmgr.GetPlayer(guid)) + { + for (uint8 i = 0; i < MAX_ARENA_SLOT; i++) + { + if(uint32 a_id = plr->GetArenaTeamId(i)) + { + if(ArenaTeam *at = objmgr.GetArenaTeamById(a_id)) + at->InspectStats(this, plr->GetGUID()); + } + } + } +} + +void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recv_data) +{ + sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_QUERY" ); + //recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 4); + + uint32 ArenaTeamId; + recv_data >> ArenaTeamId; + + ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId); + if(!arenateam) // arena team not found + return; + + arenateam->Query(this); + arenateam->Stats(this); +} + +void WorldSession::HandleArenaTeamRosterOpcode(WorldPacket & recv_data) +{ + sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_ROSTER" ); + //recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 4); + + uint32 ArenaTeamId; // arena team id + recv_data >> ArenaTeamId; + + ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId); + if(!arenateam) + return; + + arenateam->Roster(this); +} + +void WorldSession::HandleArenaTeamAddMemberOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_ARENA_TEAM_ADD_MEMBER"); + //recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 4+1); + + uint32 ArenaTeamId; // arena team id + std::string Invitedname; + + Player * player = NULL; + + recv_data >> ArenaTeamId >> Invitedname; + + if(!Invitedname.empty()) + { + if(!normalizePlayerName(Invitedname)) + return; + + player = ObjectAccessor::Instance().FindPlayerByName(Invitedname.c_str()); + } + + if(!player) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", Invitedname, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S); + return; + } + + if(player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + { + //SendArenaTeamCommandResult(ARENA_TEAM_INVITE_SS,"",Invitedname,ARENA_TEAM_PLAYER_NOT_FOUND_S); + // can't find related opcode + SendNotification("%s is not high enough level to join your team", player->GetName()); + return; + } + + ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId); + if(!arenateam) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM); + return; + } + + // OK result but not send invite + if(player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) + return; + + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != GetPlayer()->GetTeam()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_NOT_ALLIED); + return; + } + + if(player->GetArenaTeamId(arenateam->GetSlot())) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_IN_ARENA_TEAM_S); + return; + } + + if(player->GetArenaTeamIdInvited()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, player->GetName(), "", ERR_ALREADY_INVITED_TO_ARENA_TEAM_S); + return; + } + + if(arenateam->GetMembersSize() >= arenateam->GetType() * 2) + { + // should send an "arena team is full" or the likes message, I just don't know the proper values so... ERR_INTERNAL +// SendArenaTeamCommandResult(ERR_ARENA_TEAM_INVITE_SS, "", "", ERR_ARENA_TEAM_INTERNAL); + SendNotification("Your arena team is full, %s cannot join it.", player->GetName()); + return; + } + + sLog.outDebug("Player %s Invited %s to Join his ArenaTeam", GetPlayer()->GetName(), Invitedname.c_str()); + + player->SetArenaTeamIdInvited(arenateam->GetId()); + + WorldPacket data(SMSG_ARENA_TEAM_INVITE, (8+10)); + data << GetPlayer()->GetName(); + data << arenateam->GetName(); + player->GetSession()->SendPacket(&data); + + sLog.outDebug("WORLD: Sent SMSG_ARENA_TEAM_INVITE"); +} + +void WorldSession::HandleArenaTeamInviteAcceptOpcode(WorldPacket & /*recv_data*/) +{ + sLog.outDebug("CMSG_ARENA_TEAM_INVITE_ACCEPT"); // empty opcode + + ArenaTeam *at = objmgr.GetArenaTeamById(_player->GetArenaTeamIdInvited()); + if(!at) + { + // arena team not exist + return; + } + + if(_player->GetArenaTeamId(at->GetSlot())) + { + // already in arena team that size + return; + } + + // not let enemies sign petition + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != objmgr.GetPlayerTeamByGUID(at->GetCaptain())) + return; + + if(!at->AddMember(_player->GetGUID())) + return; + + // event + WorldPacket data; + BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_JOIN_SS, 2, _player->GetName(), at->GetName(), ""); + at->BroadcastPacket(&data); +} + +void WorldSession::HandleArenaTeamInviteDeclineOpcode(WorldPacket & /*recv_data*/) +{ + sLog.outDebug("CMSG_ARENA_TEAM_INVITE_DECLINE"); // empty opcode + + _player->SetArenaTeamIdInvited(0); // no more invited +} + +void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_ARENA_TEAM_LEAVE"); + //recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 4); + + uint32 ArenaTeamId; // arena team id + recv_data >> ArenaTeamId; + + ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); + if(!at) + { + // send command result + return; + } + if(_player->GetGUID() == at->GetCaptain() && at->GetMembersSize() > 1) + { + // check for correctness + SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S); + return; + } + // arena team has only one member (=captain) + if(_player->GetGUID() == at->GetCaptain()) + { + at->Disband(this); + delete at; + return; + } + + at->DelMember(_player->GetGUID()); + + // event + WorldPacket data; + BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_LEAVE_SS, 2, _player->GetName(), at->GetName(), ""); + at->BroadcastPacket(&data); + + //SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, at->GetName(), "", 0); +} + +void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_ARENA_TEAM_DISBAND"); + //recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 4); + + uint32 ArenaTeamId; // arena team id + recv_data >> ArenaTeamId; + + ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); + if(!at) + { + // arena team not found + return; + } + + if(at->GetCaptain() != _player->GetGUID()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); + return; + } + + at->Disband(this); + delete at; +} + +void WorldSession::HandleArenaTeamRemoveFromTeamOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_ARENA_TEAM_REMOVE_FROM_TEAM"); + //recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 4+1); + + uint32 ArenaTeamId; + std::string name; + + recv_data >> ArenaTeamId; + recv_data >> name; + + ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); + if(!at) + { + // arena team not found + return; + } + + uint64 guid = objmgr.GetPlayerGUIDByName(name); + if(!guid) + { + // player guid not found + return; + } + + if(at->GetCaptain() == guid) + { + // unsure + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); + return; + } + + if(at->GetCaptain() != _player->GetGUID()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); + return; + } + + if(at->GetCaptain() == guid) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S); + return; + } + + at->DelMember(guid); + + // event + WorldPacket data; + BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_REMOVE_SSS, 3, name, at->GetName(), _player->GetName()); + at->BroadcastPacket(&data); +} + +void WorldSession::HandleArenaTeamPromoteToCaptainOpcode(WorldPacket & recv_data) +{ + sLog.outDebug("CMSG_ARENA_TEAM_PROMOTE_TO_CAPTAIN"); + //recv_data.hexlike(); + + CHECK_PACKET_SIZE(recv_data, 4+1); + + uint32 ArenaTeamId; + std::string name; + + recv_data >> ArenaTeamId; + recv_data >> name; + + ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId); + if(!at) + { + // arena team not found + return; + } + + uint64 guid = objmgr.GetPlayerGUIDByName(name); + if(!guid) + { + // player guid not found + return; + } + + if(at->GetCaptain() == guid) + { + // target player already captain + return; + } + + if(at->GetCaptain() != _player->GetGUID()) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", "", ERR_ARENA_TEAM_PERMISSIONS); + return; + } + + at->SetCaptain(guid); + + // event + WorldPacket data; + BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_LEADER_CHANGED_SSS, 3, _player->GetName(), name, at->GetName()); + at->BroadcastPacket(&data); +} + +void WorldSession::SendArenaTeamCommandResult(uint32 unk1, std::string str1, std::string str2, uint32 unk3) +{ + WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+str1.length()+1+str2.length()+1+4); + data << unk1; + data << str1; + data << str2; + data << unk3; + SendPacket(&data); +} + +void WorldSession::BuildArenaTeamEventPacket(WorldPacket *data, uint8 eventid, uint8 str_count, std::string str1, std::string str2, std::string str3) +{ + data->Initialize(SMSG_ARENA_TEAM_EVENT, 1+1+1); + *data << eventid; + *data << str_count; + switch(str_count) + { + case 1: + *data << str1; + break; + case 2: + *data << str1; + *data << str2; + break; + case 3: + *data << str1; + *data << str2; + *data << str3; + break; + default: + sLog.outError("Unhandled str_count %u in SendArenaTeamEvent()", str_count); + return; + } +} + +void WorldSession::SendNotInArenaTeamPacket(uint8 type) +{ + WorldPacket data(SMSG_ARENA_ERROR, 4+1); // 886 - You are not in a %uv%u arena team + uint32 unk = 0; + data << uint32(unk); // unk(0) + if(!unk) + data << uint8(type); // team type (2=2v2,3=3v3,5=5v5), can be used for custom types... + SendPacket(&data); +} + +/* ++ERR_ARENA_NO_TEAM_II "You are not in a %dv%d arena team" + ++ERR_ARENA_TEAM_CREATE_S "%s created. To disband, use /teamdisband [2v2, 3v3, 5v5]." ++ERR_ARENA_TEAM_INVITE_SS "You have invited %s to join %s" ++ERR_ARENA_TEAM_QUIT_S "You are no longer a member of %s" +ERR_ARENA_TEAM_FOUNDER_S "Congratulations, you are a founding member of %s! To leave, use /teamquit [2v2, 3v3, 5v5]." + ++ERR_ARENA_TEAM_INTERNAL "Internal arena team error" ++ERR_ALREADY_IN_ARENA_TEAM "You are already in an arena team of that size" ++ERR_ALREADY_IN_ARENA_TEAM_S "%s is already in an arena team of that size" ++ERR_INVITED_TO_ARENA_TEAM "You have already been invited into an arena team" ++ERR_ALREADY_INVITED_TO_ARENA_TEAM_S "%s has already been invited to an arena team" ++ERR_ARENA_TEAM_NAME_INVALID "That name contains invalid characters, please enter a new name" ++ERR_ARENA_TEAM_NAME_EXISTS_S "There is already an arena team named \"%s\"" ++ERR_ARENA_TEAM_LEADER_LEAVE_S "You must promote a new team captain using /teamcaptain before leaving the team" ++ERR_ARENA_TEAM_PERMISSIONS "You don't have permission to do that" ++ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM "You are not in an arena team of that size" ++ERR_ARENA_TEAM_PLAYER_NOT_IN_TEAM_SS "%s is not in %s" ++ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S "\"%s\" not found" ++ERR_ARENA_TEAM_NOT_ALLIED "You cannot invite players from the opposing alliance" + ++ERR_ARENA_TEAM_JOIN_SS "%s has joined %s" ++ERR_ARENA_TEAM_YOU_JOIN_S "You have joined %s. To leave, use /teamquit [2v2, 3v3, 5v5]." + ++ERR_ARENA_TEAM_LEAVE_SS "%s has left %s" + ++ERR_ARENA_TEAM_LEADER_IS_SS "%s is the captain of %s" ++ERR_ARENA_TEAM_LEADER_CHANGED_SSS "%s has made %s the new captain of %s" + ++ERR_ARENA_TEAM_REMOVE_SSS "%s has been kicked out of %s by %s" + ++ERR_ARENA_TEAM_DISBANDED_S "%s has disbanded %s" + +ERR_ARENA_TEAM_TARGET_TOO_LOW_S "%s is not high enough level to join your team" + +ERR_ARENA_TEAM_TOO_MANY_MEMBERS_S "%s is full" + +ERR_ARENA_TEAM_LEVEL_TOO_LOW_I "You must be level %d to form an arena team" +*/ diff --git a/src/game/AuctionHouse.cpp b/src/game/AuctionHouse.cpp new file mode 100644 index 00000000000..9b527400b04 --- /dev/null +++ b/src/game/AuctionHouse.cpp @@ -0,0 +1,747 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Opcodes.h" +#include "Log.h" +#include "World.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "UpdateMask.h" +#include "AuctionHouseObject.h" +#include "Util.h" + +//please DO NOT use iterator++, because it is slower than ++iterator!!! +//post-incrementation is always slower than pre-incrementation ! + +//void called when player click on auctioneer npc +void WorldSession::HandleAuctionHelloOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid; //NPC guid + recv_data >> guid; + + Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_AUCTIONEER); + if (!unit) + { + sLog.outDebug( "WORLD: HandleAuctionHelloOpcode - 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); + + SendAuctionHello(guid, unit); +} + +static uint8 AuctioneerFactionToLocation(uint32 faction) +{ + if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + return 7; // neutral + + FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(faction); + if(!u_entry) + return 7; // neutral + + if(u_entry->ourMask & FACTION_MASK_ALLIANCE) + return 2; + else if(u_entry->ourMask & FACTION_MASK_HORDE) + return 6; + else + return 7; +} + +//this void causes that auction window is opened +void WorldSession::SendAuctionHello( uint64 guid, Creature* unit ) +{ + WorldPacket data( MSG_AUCTION_HELLO, 12 ); + data << (uint64) guid; + data << (uint32) AuctioneerFactionToLocation(unit->getFaction()); + SendPacket( &data ); +} + +//this function inserts to WorldPacket auction's data +bool WorldSession::SendAuctionInfo(WorldPacket & data, AuctionEntry* auction) +{ + Item *pItem = objmgr.GetAItem(auction->item_guidlow); + if (!pItem) + { + sLog.outError("auction to item, that doesn't exist !!!!"); + return false; + } + data << auction->Id; + data << pItem->GetUInt32Value(OBJECT_FIELD_ENTRY); + + for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; i++) + { + data << (uint32) pItem->GetEnchantmentId(EnchantmentSlot(i)); + data << (uint32) pItem->GetEnchantmentDuration(EnchantmentSlot(i)); + data << (uint32) pItem->GetEnchantmentCharges(EnchantmentSlot(i)); + } + + data << (uint32) pItem->GetItemRandomPropertyId(); //random item property id + data << (uint32) pItem->GetItemSuffixFactor(); //SuffixFactor + data << (uint32) pItem->GetCount(); //item->count + data << (uint32) pItem->GetSpellCharges(); //item->charge FFFFFFF + data << (uint32) 0; //Unknown + data << (uint64) auction->owner; //Auction->owner + data << (uint32) auction->startbid; //Auction->startbid (not sure if useful) + data << (uint32) ((auction->bid)? objmgr.GetAuctionOutBid(auction->bid) : 0); + //minimal outbid + data << (uint32) auction->buyout; //auction->buyout + data << (uint32) (auction->time - time(NULL)) * 1000; //time left + data << (uint64) auction->bidder; //auction->bidder current + data << (uint32) auction->bid; //current bid + return true; +} + +//call this method when player bids, creates, or deletes auction +void WorldSession::SendAuctionCommandResult(uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError ) +{ + WorldPacket data( SMSG_AUCTION_COMMAND_RESULT, 16 ); + data << auctionId; + data << Action; + data << ErrorCode; + if ( !ErrorCode && Action ) + data << bidError; //when bid, then send 0, once... + SendPacket(&data); +} + +//this function sends notification, if bidder is online +void WorldSession::SendAuctionBidderNotification( uint32 location, uint32 auctionId, uint64 bidder, uint32 bidSum, uint32 diff, uint32 item_template) +{ + WorldPacket data(SMSG_AUCTION_BIDDER_NOTIFICATION, (8*4)); + data << location; + data << auctionId; + data << (uint64) bidder; + data << bidSum; + data << (uint32) diff; + data << item_template; + data << (uint32) 0; + SendPacket(&data); +} + +//this void causes on client to display: "Your auction sold" +void WorldSession::SendAuctionOwnerNotification( AuctionEntry* auction) +{ + WorldPacket data(SMSG_AUCTION_OWNER_NOTIFICATION, (7*4)); + data << auction->Id; + data << auction->bid; + data << (uint32) 0; //unk + data << (uint32) 0; //unk + data << (uint32) 0; //unk + data << auction->item_template; + data << (uint32) 0; //unk + SendPacket(&data); +} + +//this function sends mail to old bidder +void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPrice) +{ + uint64 oldBidder_guid = MAKE_NEW_GUID(auction->bidder,0, HIGHGUID_PLAYER); + Player *oldBidder = objmgr.GetPlayer(oldBidder_guid); + + uint32 oldBidder_accId = 0; + if(!oldBidder) + oldBidder_accId = objmgr.GetPlayerAccountIdByGUID(oldBidder_guid); + + // old bidder exist + if(oldBidder || oldBidder_accId) + { + std::ostringstream msgAuctionOutbiddedSubject; + msgAuctionOutbiddedSubject << auction->item_template << ":0:" << AUCTION_OUTBIDDED; + + if (oldBidder) + oldBidder->GetSession()->SendAuctionBidderNotification( auction->location, auction->Id, _player->GetGUID(), newPrice, objmgr.GetAuctionOutBid(auction->bid), auction->item_template); + + WorldSession::SendMailTo(oldBidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->bidder, msgAuctionOutbiddedSubject.str(), 0, NULL, auction->bid, 0, MAIL_CHECK_MASK_NONE); + } +} + +//this function sends mail, when auction is cancelled to old bidder +void WorldSession::SendAuctionCancelledToBidderMail( AuctionEntry* auction ) +{ + uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER); + Player *bidder = objmgr.GetPlayer(bidder_guid); + + uint32 bidder_accId = 0; + if(!bidder) + bidder_accId = objmgr.GetPlayerAccountIdByGUID(bidder_guid); + + // bidder exist + if(bidder || bidder_accId) + { + std::ostringstream msgAuctionCancelledSubject; + msgAuctionCancelledSubject << auction->item_template << ":0:" << AUCTION_CANCELLED_TO_BIDDER; + + WorldSession::SendMailTo(bidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, auction->bidder, msgAuctionCancelledSubject.str(), 0, NULL, auction->bid, 0, MAIL_CHECK_MASK_NONE); + } +} + +//this void creates new auction and adds auction to some auctionhouse +void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+8+4+4+4); + + uint64 auctioneer, item; + uint32 etime, bid, buyout; + recv_data >> auctioneer >> item; + recv_data >> bid >> buyout >> etime; + Player *pl = GetPlayer(); + + if (!item || !bid || !etime) + return; //check for cheaters + + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, auctioneer,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug( "WORLD: HandleAuctionSellItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)) ); + return; + } + + // client send time in minutes, convert to common used sec time + etime *= MINUTE; + + // client understand only 3 auction time + switch(etime) + { + case 1*MIN_AUCTION_TIME: + case 2*MIN_AUCTION_TIME: + case 4*MIN_AUCTION_TIME: + break; + default: + return; + } + + // remove fake death + if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + + Item *it = pl->GetItemByGuid( item ); + //do not allow to sell already auctioned items + if(objmgr.GetAItem(GUID_LOPART(item))) + { + sLog.outError("AuctionError, player %s is sending item id: %u, but item is already in another auction", pl->GetName(), GUID_LOPART(item)); + SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); + return; + } + // prevent sending bag with items (cheat: can be placed in bag after adding equiped empty bag to auction) + if(!it) + { + SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_ITEM_NOT_FOUND); + return; + } + + if(!it->CanBeTraded()) + { + SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); + return; + } + + if (it->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION)) + { + SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); + return; + } + + uint32 location = AuctioneerFactionToLocation(pCreature->getFaction()); + AuctionHouseObject * mAuctions; + mAuctions = objmgr.GetAuctionsMap( location ); + + //we have to take deposit : + uint32 deposit = objmgr.GetAuctionDeposit( location, etime, it ); + if ( pl->GetMoney() < deposit ) + { + SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY); + return; + } + + if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) + { + sLog.outCommand("GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)", + GetPlayerName(),GetAccountId(),it->GetProto()->Name1,it->GetEntry(),it->GetCount()); + } + + pl->ModifyMoney( -int32(deposit) ); + + uint32 auction_time = uint32(etime * sWorld.getRate(RATE_AUCTION_TIME)); + + AuctionEntry *AH = new AuctionEntry; + AH->Id = objmgr.GenerateAuctionID(); + AH->auctioneer = GUID_LOPART(auctioneer); + AH->item_guidlow = GUID_LOPART(item); + AH->item_template = it->GetEntry(); + AH->owner = pl->GetGUIDLow(); + AH->startbid = bid; + AH->bidder = 0; + AH->bid = 0; + AH->buyout = buyout; + AH->time = time(NULL) + auction_time; + AH->deposit = deposit; + AH->location = location; + + sLog.outDetail("selling item %u to auctioneer %u with initial bid %u with buyout %u and with time %u (in sec) in location: %u", GUID_LOPART(item), GUID_LOPART(auctioneer), bid, buyout, auction_time, location); + mAuctions->AddAuction(AH); + + objmgr.AddAItem(it); + pl->MoveItemFromInventory( it->GetBagSlot(), it->GetSlot(), true); + + CharacterDatabase.BeginTransaction(); + it->DeleteFromInventoryDB(); + it->SaveToDB(); // recursive and not have transaction guard into self, not in inventiory and can be save standalone + CharacterDatabase.PExecute("INSERT INTO auctionhouse (id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit,location) " + "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '" I64FMTD "', '%u', '%u', '%u', '%u', '%u')", + AH->Id, AH->auctioneer, AH->item_guidlow, AH->item_template, AH->owner, AH->buyout, (uint64)AH->time, AH->bidder, AH->bid, AH->startbid, AH->deposit, AH->location); + pl->SaveInventoryAndGoldToDB(); + CharacterDatabase.CommitTransaction(); + + SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, AUCTION_OK); +} + +//this function is called when client bids or buys out auction +void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4+4); + + uint64 auctioneer; + uint32 auctionId; + uint32 price; + recv_data >> auctioneer; + recv_data >> auctionId >> price; + + if (!auctionId || !price) + return; //check for cheaters + + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, auctioneer,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug( "WORLD: HandleAuctionPlaceBid - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)) ); + return; + } + + // remove fake death + if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + + uint32 location = AuctioneerFactionToLocation(pCreature->getFaction()); + + AuctionHouseObject * mAuctions; + mAuctions = objmgr.GetAuctionsMap( location ); + + AuctionEntry *auction = mAuctions->GetAuction(auctionId); + Player *pl = GetPlayer(); + + if( !auction || auction->owner == pl->GetGUIDLow() ) + { + //you cannot bid your own auction: + SendAuctionCommandResult( 0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR ); + return; + } + + // impossible have online own another character (use this for speedup check in case online owner) + Player* auction_owner = objmgr.GetPlayer(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)); + if( !auction_owner && objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == pl->GetSession()->GetAccountId()) + { + //you cannot bid your another character auction: + SendAuctionCommandResult( 0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR ); + return; + } + + if (price < (auction->bid + objmgr.GetAuctionOutBid(auction->bid))) + { + //auction has already higher bid, client tests it! + //SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???); + return; + } + + if (price > pl->GetMoney()) + { + //you don't have enought money!, client tests! + //SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???); + return; + } + + if ((price < auction->buyout) || (auction->buyout == 0)) + { + if (auction->bidder > 0) + { + if ( auction->bidder == pl->GetGUIDLow() ) + { + pl->ModifyMoney( -int32(price - auction->bid)); + } + else + { + // mail to last bidder and return money + SendAuctionOutbiddedMail( auction , price ); + pl->ModifyMoney( -int32(price) ); + } + } + else + { + pl->ModifyMoney( -int32(price) ); + } + auction->bidder = pl->GetGUIDLow(); + auction->bid = price; + + // after this update we should save player's money ... + CharacterDatabase.PExecute("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); + + SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0 ); + } + else + { + //buyout: + if (pl->GetGUIDLow() == auction->bidder ) + { + pl->ModifyMoney(-int32(auction->buyout - auction->bid)); + } + else + { + pl->ModifyMoney(-int32(auction->buyout)); + if ( auction->bidder ) //buyout for bidded auction .. + { + SendAuctionOutbiddedMail( auction, auction->buyout ); + } + } + auction->bidder = pl->GetGUIDLow(); + auction->bid = auction->buyout; + + objmgr.SendAuctionSalePendingMail( auction ); + objmgr.SendAuctionSuccessfulMail( auction ); + objmgr.SendAuctionWonMail( auction ); + + SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK); + + objmgr.RemoveAItem(auction->item_guidlow); + mAuctions->RemoveAuction(auction->Id); + CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",auction->Id); + + delete auction; + } + CharacterDatabase.BeginTransaction(); + pl->SaveInventoryAndGoldToDB(); + CharacterDatabase.CommitTransaction(); +} + +//this void is called when auction_owner cancels his auction +void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint64 auctioneer; + uint32 auctionId; + recv_data >> auctioneer; + recv_data >> auctionId; + //sLog.outDebug( "Cancel AUCTION AuctionID: %u", auctionId); + + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, auctioneer,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug( "WORLD: HandleAuctionRemoveItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer)) ); + return; + } + + // remove fake death + if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + + uint32 location = AuctioneerFactionToLocation(pCreature->getFaction()); + + AuctionHouseObject * mAuctions; + mAuctions = objmgr.GetAuctionsMap( location ); + + AuctionEntry *auction = mAuctions->GetAuction(auctionId); + Player *pl = GetPlayer(); + + if (auction && auction->owner == pl->GetGUIDLow()) + { + Item *pItem = objmgr.GetAItem(auction->item_guidlow); + if (pItem) + { + if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid + { + uint32 auctionCut = objmgr.GetAuctionCut( auction->location, auction->bid); + if ( pl->GetMoney() < auctionCut ) //player doesn't have enough money, maybe message needed + return; + //some auctionBidderNotification would be needed, but don't know that parts.. + SendAuctionCancelledToBidderMail( auction ); + pl->ModifyMoney( -int32(auctionCut) ); + } + // Return the item by mail + std::ostringstream msgAuctionCanceledOwner; + msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED; + + MailItemsInfo mi; + mi.AddItem(auction->item_guidlow, auction->item_template, pItem); + + // item will deleted or added to received mail list + WorldSession::SendMailTo(pl, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->location, pl->GetGUIDLow(), msgAuctionCanceledOwner.str(), 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE); + } + else + { + sLog.outError("Auction id: %u has non-existed item (item guid : %u)!!!", auction->Id, auction->item_guidlow); + SendAuctionCommandResult( 0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR ); + return; + } + } + else + { + SendAuctionCommandResult( 0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR ); + //this code isn't possible ... maybe there should be assert + sLog.outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", pl->GetGUIDLow(), auctionId ); + return; + } + + //inform player, that auction is removed + SendAuctionCommandResult( auction->Id, AUCTION_CANCEL, AUCTION_OK ); + // Now remove the auction + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",auction->Id); + pl->SaveInventoryAndGoldToDB(); + CharacterDatabase.CommitTransaction(); + objmgr.RemoveAItem( auction->item_guidlow ); + mAuctions->RemoveAuction( auction->Id ); + delete auction; +} + +//called when player lists his bids +void WorldSession::HandleAuctionListBidderItems( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4+4); + + uint64 guid; //NPC guid + uint32 listfrom; //page of auctions + uint32 outbiddedCount; //count of outbidded auctions + + recv_data >> guid; + recv_data >> listfrom; // not used in fact (this list not have page control in client) + recv_data >> outbiddedCount; + if (recv_data.size() != (16 + outbiddedCount * 4 )) + { + sLog.outError("Client sent bad opcode!!! with count: %u and size : %d (mustbe: %d", outbiddedCount, recv_data.size(),(16 + outbiddedCount * 4 )); + outbiddedCount = 0; + } + + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug( "WORLD: HandleAuctionListBidderItems - 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); + + uint32 location = AuctioneerFactionToLocation(pCreature->getFaction()); + AuctionHouseObject* mAuctions = objmgr.GetAuctionsMap( location ); + + WorldPacket data( SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4) ); + Player *pl = GetPlayer(); + data << (uint32) 0; //add 0 as count + uint32 count = 0; + uint32 totalcount = 0; + while ( outbiddedCount > 0) //add all data, which client requires + { + --outbiddedCount; + uint32 outbiddedAuctionId; + recv_data >> outbiddedAuctionId; + AuctionEntry * auction = mAuctions->GetAuction( outbiddedAuctionId ); + if ( auction && SendAuctionInfo(data, auction)) + { + ++totalcount; + ++count; + } + } + for (AuctionHouseObject::AuctionEntryMap::iterator itr = mAuctions->GetAuctionsBegin();itr != mAuctions->GetAuctionsEnd();++itr) + { + AuctionEntry *Aentry = itr->second; + if( Aentry && Aentry->bidder == pl->GetGUIDLow() ) + { + if (SendAuctionInfo(data, itr->second)) + ++count; + ++totalcount; + } + } + data.put( 0, count ); // add count to placeholder + data << totalcount; + data << (uint32)300; //unk 2.3.0 + SendPacket(&data); +} + +//this void sends player info about his auctions +void WorldSession::HandleAuctionListOwnerItems( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint32 listfrom; + uint64 guid; + + recv_data >> guid; + recv_data >> listfrom; // not used in fact (this list not have page control in client) + + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug( "WORLD: HandleAuctionListOwnerItems - 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); + + uint32 location = AuctioneerFactionToLocation(pCreature->getFaction()); + + AuctionHouseObject* mAuctions = objmgr.GetAuctionsMap( location ); + + WorldPacket data( SMSG_AUCTION_OWNER_LIST_RESULT, (4+4+4) ); + data << (uint32) 0; // amount place holder + + uint32 count = 0; + uint32 totalcount = 0; + for (AuctionHouseObject::AuctionEntryMap::iterator itr = mAuctions->GetAuctionsBegin();itr != mAuctions->GetAuctionsEnd();++itr) + { + AuctionEntry *Aentry = itr->second; + if( Aentry && Aentry->owner == _player->GetGUIDLow() ) + { + if(SendAuctionInfo(data, itr->second)) + ++count; + ++totalcount; + } + } + data.put(0, count); + data << (uint32) totalcount; + data << (uint32) 0; + SendPacket(&data); +} + +//this void is called when player clicks on search button +void WorldSession::HandleAuctionListItems( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4+1+1+1+4+4+4+4+1); + + std::string searchedname, name; + uint8 levelmin, levelmax, usable, location; + uint32 count, totalcount, listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality; + uint64 guid; + + recv_data >> guid; + recv_data >> listfrom; // start, used for page control listing by 50 elements + recv_data >> searchedname; + + // recheck with known string size + CHECK_PACKET_SIZE(recv_data,8+4+(searchedname.size()+1)+1+1+4+4+4+4+1); + + recv_data >> levelmin >> levelmax; + recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory; + recv_data >> quality >> usable; + + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_AUCTIONEER); + if (!pCreature) + { + sLog.outDebug( "WORLD: HandleAuctionListItems - 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); + + location = AuctioneerFactionToLocation(pCreature->getFaction()); + AuctionHouseObject * mAuctions; + mAuctions = objmgr.GetAuctionsMap( location ); + + //sLog.outDebug("Auctionhouse search guid: " I64FMTD ", list from: %u, searchedname: %s, levelmin: %u, levelmax: %u, auctionSlotID: %u, auctionMainCategory: %u, auctionSubCategory: %u, quality: %u, usable: %u", guid, listfrom, searchedname.c_str(), levelmin, levelmax, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, usable); + + WorldPacket data( SMSG_AUCTION_LIST_RESULT, (4+4+4) ); + count = 0; + totalcount = 0; + data << (uint32) 0; + + // converting string that we try to find to lower case + std::wstring wsearchedname; + if(!Utf8toWStr(searchedname,wsearchedname)) + return; + + wstrToLower(wsearchedname); + + for (AuctionHouseObject::AuctionEntryMap::iterator itr = mAuctions->GetAuctionsBegin();itr != mAuctions->GetAuctionsEnd();++itr) + { + AuctionEntry *Aentry = itr->second; + Item *item = objmgr.GetAItem(Aentry->item_guidlow); + if( item ) + { + ItemPrototype const *proto = item->GetProto(); + if( proto ) + { + if( auctionMainCategory == (0xffffffff) || proto->Class == auctionMainCategory ) + { + if( auctionSubCategory == (0xffffffff) || proto->SubClass == auctionSubCategory ) + { + if( auctionSlotID == (0xffffffff) || proto->InventoryType == auctionSlotID ) + { + if( quality == (0xffffffff) || proto->Quality == quality ) + { + if( usable == (0x00) || _player->CanUseItem( item ) == EQUIP_ERR_OK ) + { + if( ( levelmin == (0x00) || proto->RequiredLevel >= levelmin ) && ( levelmax == (0x00) || proto->RequiredLevel <= levelmax ) ) + { + name = proto->Name1; + + // local name + int loc_idx = GetSessionDbLocaleIndex(); + if ( loc_idx >= 0 ) + { + ItemLocale const *il = objmgr.GetItemLocale(proto->ItemId); + if (il) + { + if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty()) + name = il->Name[loc_idx]; + } + } + + if(name.empty()) + continue; + + if( wsearchedname.empty() || Utf8FitTo(name, wsearchedname) ) + { + if ((count < 50) && (totalcount >= listfrom)) + { + ++count; + SendAuctionInfo( data, Aentry); + } + ++totalcount; + } + } + } + } + } + } + } + } + } + } + data.put(0, count); + data << (uint32) totalcount; + data << (uint32) 300; // unk 2.3.0 const? + SendPacket(&data); +} diff --git a/src/game/AuctionHouseObject.h b/src/game/AuctionHouseObject.h new file mode 100644 index 00000000000..504c41c62e5 --- /dev/null +++ b/src/game/AuctionHouseObject.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AUCTION_HOUSE_H +#define _AUCTION_HOUSE_H + +#include "SharedDefines.h" + +#define MIN_AUCTION_TIME (12*HOUR) + +enum AuctionError +{ + AUCTION_OK = 0, + AUCTION_INTERNAL_ERROR = 2, + AUCTION_NOT_ENOUGHT_MONEY = 3, + AUCTION_ITEM_NOT_FOUND = 4, + CANNOT_BID_YOUR_AUCTION_ERROR = 10 +}; + +enum AuctionAction +{ + AUCTION_SELL_ITEM = 0, + AUCTION_CANCEL = 1, + AUCTION_PLACE_BID = 2 +}; + +struct AuctionEntry +{ + uint32 Id; + uint32 auctioneer; + uint32 item_guidlow; + uint32 item_template; + uint32 owner; + uint32 startbid; //maybe useless + uint32 bid; + uint32 buyout; + time_t time; + uint32 bidder; + uint32 deposit; //deposit can be calculated only when creating auction + uint32 location; +}; + +//this class is used as auctionhouse instance +class AuctionHouseObject +{ + public: + AuctionHouseObject() {} + ~AuctionHouseObject() + { + for (AuctionEntryMap::iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) + delete itr->second; + } + + typedef std::map AuctionEntryMap; + + uint32 Getcount() { return AuctionsMap.size(); } + + AuctionEntryMap::iterator GetAuctionsBegin() {return AuctionsMap.begin();} + AuctionEntryMap::iterator GetAuctionsEnd() {return AuctionsMap.end();} + + void AddAuction(AuctionEntry *ah) + { + ASSERT( ah ); + AuctionsMap[ah->Id] = ah; + } + + AuctionEntry* GetAuction(uint32 id) const + { + AuctionEntryMap::const_iterator itr = AuctionsMap.find( id ); + if( itr != AuctionsMap.end() ) + return itr->second; + return NULL; + } + + bool RemoveAuction(uint32 id) + { + AuctionEntryMap::iterator i = AuctionsMap.find(id); + if (i == AuctionsMap.end()) + { + return false; + } + AuctionsMap.erase(i); + return true; + } + + private: + AuctionEntryMap AuctionsMap; +}; +#endif diff --git a/src/game/Bag.cpp b/src/game/Bag.cpp new file mode 100644 index 00000000000..a529d075dcf --- /dev/null +++ b/src/game/Bag.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Bag.h" +#include "ObjectMgr.h" +#include "Database/DatabaseEnv.h" +#include "Log.h" +#include "WorldPacket.h" +#include "UpdateData.h" +#include "WorldSession.h" + +Bag::Bag( ): Item() +{ + m_objectType |= TYPEMASK_CONTAINER; + m_objectTypeId = TYPEID_CONTAINER; + + m_valuesCount = CONTAINER_END; + + memset(m_bagslot, 0, sizeof(Item *) * MAX_BAG_SIZE); // Maximum 20 Slots +} + +Bag::~Bag() +{ + for(int i = 0; iAddToWorld(); + } +} + +void Bag::RemoveFromWorld() +{ + for(int i = 0; iRemoveFromWorld(); + } + + Item::RemoveFromWorld(); +} + +bool Bag::Create(uint32 guidlow, uint32 itemid, Player const* owner) +{ + ItemPrototype const * itemProto = objmgr.GetItemPrototype(itemid); + + if(!itemProto || itemProto->ContainerSlots > MAX_BAG_SIZE) + return false; + + Object::_Create( guidlow, 0, HIGHGUID_CONTAINER ); + + SetUInt32Value(OBJECT_FIELD_ENTRY, itemid); + SetFloatValue(OBJECT_FIELD_SCALE_X, 1.0f); + + SetUInt64Value(ITEM_FIELD_OWNER, owner ? owner->GetGUID() : 0); + SetUInt64Value(ITEM_FIELD_CONTAINED, owner ? owner->GetGUID() : 0); + + SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability); + SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability); + SetUInt32Value(ITEM_FIELD_FLAGS, itemProto->Flags); + SetUInt32Value(ITEM_FIELD_STACK_COUNT, 1); + + // Setting the number of Slots the Container has + SetUInt32Value(CONTAINER_FIELD_NUM_SLOTS, itemProto->ContainerSlots); + + // Cleanning 20 slots + for (uint8 i = 0; i < MAX_BAG_SIZE; i++) + { + SetUInt64Value(CONTAINER_FIELD_SLOT_1 + (i*2), 0); + m_bagslot[i] = NULL; + } + + return true; +} + +void Bag::SaveToDB() +{ + Item::SaveToDB(); +} + +bool Bag::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result) +{ + if(!Item::LoadFromDB(guid, owner_guid, result)) + return false; + + // cleanup bag content related item value fields (its will be filled correctly from `character_inventory`) + for (uint32 i = 0; i < GetProto()->ContainerSlots; i++) + { + SetUInt64Value(CONTAINER_FIELD_SLOT_1 + (i*2), 0); + if (m_bagslot[i]) + { + delete m_bagslot[i]; + m_bagslot[i] = NULL; + } + } + + return true; +} + +void Bag::DeleteFromDB() +{ + for (int i = 0; i < MAX_BAG_SIZE; i++) + { + if (m_bagslot[i]) + { + m_bagslot[i]->DeleteFromDB(); + } + } + + Item::DeleteFromDB(); +} + +uint32 Bag::GetFreeSlots() const +{ + uint32 ContainerSlots=GetProto()->ContainerSlots; + uint32 slots = 0; + for (uint8 i=0; i SetContainer(NULL); + + m_bagslot[slot] = NULL; + SetUInt64Value( CONTAINER_FIELD_SLOT_1 + (slot * 2), 0 ); +} + +void Bag::StoreItem( uint8 slot, Item *pItem, bool /*update*/ ) +{ + assert(slot < MAX_BAG_SIZE); + + if( pItem ) + { + m_bagslot[slot] = pItem; + SetUInt64Value(CONTAINER_FIELD_SLOT_1 + (slot * 2), pItem->GetGUID()); + pItem->SetUInt64Value(ITEM_FIELD_CONTAINED, GetGUID()); + pItem->SetUInt64Value( ITEM_FIELD_OWNER, GetOwnerGUID() ); + pItem->SetContainer(this); + pItem->SetSlot(slot); + } +} + +void Bag::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const +{ + Item::BuildCreateUpdateBlockForPlayer( data, target ); + + for (int i = 0; i < MAX_BAG_SIZE; i++) + { + if(m_bagslot[i]) + m_bagslot[i]->BuildCreateUpdateBlockForPlayer( data, target ); + } +} + +// If the bag is empty returns true +bool Bag::IsEmpty() const +{ + uint32 ContainerSlots=GetProto()->ContainerSlots; + for(uint32 i=0; i < ContainerSlots; i++) + if (m_bagslot[i]) return false; + + return true; +} + +uint32 Bag::GetItemCount( uint32 item, Item* eItem ) const +{ + uint32 ContainerSlots=GetProto()->ContainerSlots; + + Item *pItem; + uint32 count = 0; + for(uint32 i=0; i < ContainerSlots; i++) + { + pItem = m_bagslot[i]; + if( pItem && pItem != eItem && pItem->GetEntry() == item ) + count += pItem->GetCount(); + } + + if(eItem && eItem->GetProto()->GemProperties) + { + for(uint32 i=0; i < ContainerSlots; i++) + { + pItem = m_bagslot[i]; + if( pItem && pItem != eItem && pItem->GetProto()->Socket[0].Color ) + count += pItem->GetGemCountWithID(item); + } + } + + return count; +} + +uint8 Bag::GetSlotByItemGUID(uint64 guid) const +{ + uint32 ContainerSlots=GetProto()->ContainerSlots; + + for(uint32 i=0;iGetGUID() == guid) + return i; + } + return NULL_SLOT; +} + +// Adds an item to a bag slot +// - slot can be NULL_SLOT, in that case function searchs for a free slot +// - Return values: 0 - item not added +// 1 - item added to a free slot (and perhaps to a stack) +// 2 - item added to a stack (item should be deleted) +Item* Bag::GetItemByPos( uint8 slot ) const +{ + ItemPrototype const *pBagProto = GetProto(); + if( pBagProto ) + { + if( slot < pBagProto->ContainerSlots ) + return m_bagslot[slot]; + } + return NULL; +} diff --git a/src/game/Bag.h b/src/game/Bag.h new file mode 100644 index 00000000000..cc7ca52b338 --- /dev/null +++ b/src/game/Bag.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_BAG_H +#define MANGOS_BAG_H + +// Maximum 36 Slots ( (CONTAINER_END - CONTAINER_FIELD_SLOT_1)/2 +#define MAX_BAG_SIZE 36 // 2.0.12 + +#include "Object.h" +#include "ItemPrototype.h" +#include "Unit.h" +#include "Creature.h" +#include "Item.h" + +class Bag : public Item +{ + public: + + Bag(); + ~Bag(); + + void AddToWorld(); + void RemoveFromWorld(); + + bool Create(uint32 guidlow, uint32 itemid, Player const* owner); + + void Clear(); + void StoreItem( uint8 slot, Item *pItem, bool update ); + void RemoveItem( uint8 slot, bool update ); + + Item* GetItemByPos( uint8 slot ) const; + uint32 GetItemCount( uint32 item, Item* eItem = NULL ) const; + + uint8 GetSlotByItemGUID(uint64 guid) const; + bool IsEmpty() const; + uint32 GetFreeSlots() const; + + // DB operations + // overwrite virtual Item::SaveToDB + void SaveToDB(); + // overwrite virtual Item::LoadFromDB + bool LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result = NULL); + // overwrite virtual Item::DeleteFromDB + void DeleteFromDB(); + + void BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) const; + + protected: + + // Bag Storage space + Item* m_bagslot[MAX_BAG_SIZE]; +}; + +inline Item* NewItemOrBag(ItemPrototype const * proto) +{ + return (proto->InventoryType == INVTYPE_BAG) ? new Bag : new Item; +} +#endif diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp new file mode 100644 index 00000000000..589b4175049 --- /dev/null +++ b/src/game/BattleGround.cpp @@ -0,0 +1,1153 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Object.h" +#include "Player.h" +#include "BattleGround.h" +#include "Creature.h" +#include "MapManager.h" +#include "Language.h" +#include "Chat.h" +#include "SpellAuras.h" +#include "World.h" +#include "Util.h" + +BattleGround::BattleGround() +{ + m_TypeID = 0; + m_InstanceID = 0; + m_Status = 0; + m_EndTime = 0; + m_LastResurrectTime = 0; + m_Queue_type = MAX_BATTLEGROUND_QUEUES; + m_InvitedAlliance = 0; + m_InvitedHorde = 0; + m_ArenaType = 0; + m_IsArena = false; + m_Winner = 2; + m_StartTime = 0; + m_Events = 0; + m_IsRated = false; + m_BuffChange = false; + m_Name = ""; + m_LevelMin = 0; + m_LevelMax = 0; + + m_MaxPlayersPerTeam = 0; + m_MaxPlayers = 0; + m_MinPlayersPerTeam = 0; + m_MinPlayers = 0; + + m_MapId = 0; + + m_TeamStartLocX[BG_TEAM_ALLIANCE] = 0; + m_TeamStartLocX[BG_TEAM_HORDE] = 0; + + m_TeamStartLocY[BG_TEAM_ALLIANCE] = 0; + m_TeamStartLocY[BG_TEAM_HORDE] = 0; + + m_TeamStartLocZ[BG_TEAM_ALLIANCE] = 0; + m_TeamStartLocZ[BG_TEAM_HORDE] = 0; + + m_TeamStartLocO[BG_TEAM_ALLIANCE] = 0; + m_TeamStartLocO[BG_TEAM_HORDE] = 0; + + m_BgRaids[BG_TEAM_ALLIANCE] = NULL; + m_BgRaids[BG_TEAM_HORDE] = NULL; + + m_PlayersCount[BG_TEAM_ALLIANCE] = 0; + m_PlayersCount[BG_TEAM_HORDE] = 0; +} + +BattleGround::~BattleGround() +{ + +} + +void BattleGround::Update(time_t diff) +{ + + if(!GetPlayersSize() && !GetRemovedPlayersSize() && !GetReviveQueueSize()) + //BG is empty + return; + + WorldPacket data; + + if(GetRemovedPlayersSize()) + { + for(std::map::iterator itr = m_RemovedPlayers.begin(); itr != m_RemovedPlayers.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + switch(itr->second) + { + //following code is handled by event: + /*case 0: + sBattleGroundMgr.m_BattleGroundQueues[GetTypeID()].RemovePlayer(itr->first); + //RemovePlayerFromQueue(itr->first); + if(plr) + { + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_NONE, 0, 0); + plr->GetSession()->SendPacket(&data); + } + break;*/ + case 1: // currently in bg and was removed from bg + if(plr) + RemovePlayerAtLeave(itr->first, true, true); + else + RemovePlayerAtLeave(itr->first, false, false); + break; + case 2: // revive queue + RemovePlayerFromResurrectQueue(itr->first); + break; + default: + sLog.outError("BattleGround: Unknown remove player case!"); + } + } + m_RemovedPlayers.clear(); + } + + // this code isn't efficient and its idea isn't implemented yet + /* offline players are removed from battleground in worldsession::LogoutPlayer() + // remove offline players from bg after ~5 minutes + if(GetPlayersSize()) + { + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + itr->second.LastOnlineTime += diff; + + if(plr) + itr->second.LastOnlineTime = 0; // update last online time + else + if(itr->second.LastOnlineTime >= MAX_OFFLINE_TIME) // 5 minutes + m_RemovedPlayers[itr->first] = 1; // add to remove list (BG) + } + }*/ + + m_LastResurrectTime += diff; + if (m_LastResurrectTime >= RESURRECTION_INTERVAL) + { + if(GetReviveQueueSize()) + { + for(std::map >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr) + { + Creature *sh = NULL; + for(std::vector::iterator itr2 = (itr->second).begin(); itr2 != (itr->second).end(); ++itr2) + { + Player *plr = objmgr.GetPlayer(*itr2); + if(!plr) + continue; + + if (!sh) + { + sh = ObjectAccessor::GetCreature(*plr, itr->first); + // only for visual effect + if (sh) + sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true); // Spirit Heal, effect 117 + } + + plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true); // Resurrection visual + m_ResurrectQueue.push_back(*itr2); + } + (itr->second).clear(); + } + + m_ReviveQueue.clear(); + m_LastResurrectTime = 0; + } + else + // queue is clear and time passed, just update last resurrection time + m_LastResurrectTime = 0; + } + else if (m_LastResurrectTime > 500) // Resurrect players only half a second later, to see spirit heal effect on NPC + { + for(std::vector::iterator itr = m_ResurrectQueue.begin(); itr != m_ResurrectQueue.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(*itr); + if(!plr) + continue; + plr->ResurrectPlayer(1.0f); + plr->CastSpell(plr, SPELL_SPIRIT_HEAL_MANA, true); + ObjectAccessor::Instance().ConvertCorpseForPlayer(*itr); + } + m_ResurrectQueue.clear(); + } + + if(GetStatus() == STATUS_WAIT_LEAVE) + { + // remove all players from battleground after 2 minutes + m_EndTime += diff; + if(m_EndTime >= TIME_TO_AUTOREMOVE) // 2 minutes + { + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + m_RemovedPlayers[itr->first] = 1; // add to remove list (BG) + } + // do not change any battleground's private variables + } + } +} + +void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O) +{ + uint8 idx = GetTeamIndexByTeamId(TeamID); + m_TeamStartLocX[idx] = X; + m_TeamStartLocY[idx] = Y; + m_TeamStartLocZ[idx] = Z; + m_TeamStartLocO[idx] = O; +} + +void BattleGround::SendPacketToAll(WorldPacket *packet) +{ + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + if(plr) + plr->GetSession()->SendPacket(packet); + else + sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); + } +} + +void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self) +{ + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + + if(!plr) + { + sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); + continue; + } + + if(!self && sender == plr) + continue; + + if(plr->GetTeam() == TeamID) + plr->GetSession()->SendPacket(packet); + } +} + +void BattleGround::PlaySoundToAll(uint32 SoundID) +{ + WorldPacket data; + sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID); + SendPacketToAll(&data); +} + +void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID) +{ + WorldPacket data; + + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + + if(!plr) + { + sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); + continue; + } + + if(plr->GetTeam() == TeamID) + { + sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID); + plr->GetSession()->SendPacket(&data); + } + } +} + +void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID) +{ + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + + if(!plr) + { + sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); + continue; + } + + if(plr->GetTeam() == TeamID) + plr->CastSpell(plr, SpellID, true); + } +} + +void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID) +{ + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + + if(!plr) + { + sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); + continue; + } + + if(plr->GetTeam() == TeamID) + UpdatePlayerScore(plr, SCORE_BONUS_HONOR, Honor); + } +} + +void BattleGround::RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID) +{ + FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id); + + if(!factionEntry) + return; + + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + + if(!plr) + { + sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); + continue; + } + + if(plr->GetTeam() == TeamID) + plr->ModifyFactionReputation(factionEntry, Reputation); + } +} + +void BattleGround::UpdateWorldState(uint32 Field, uint32 Value) +{ + WorldPacket data; + sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value); + SendPacketToAll(&data); +} + +void BattleGround::UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source) +{ + WorldPacket data; + sBattleGroundMgr.BuildUpdateWorldStatePacket(&data, Field, Value); + Source->GetSession()->SendPacket(&data); +} + +void BattleGround::EndBattleGround(uint32 winner) +{ + WorldPacket data; + Player *Source = NULL; + const char *winmsg = ""; + + if(winner == ALLIANCE) + { + winmsg = GetMangosString(LANG_BG_A_WINS); + + PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound + + SetWinner(WINNER_ALLIANCE); + } + else + { + winmsg = GetMangosString(LANG_BG_H_WINS); + + PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound + + SetWinner(WINNER_HORDE); + } + + SetStatus(STATUS_WAIT_LEAVE); + m_EndTime = 0; + + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + if(!plr) + { + sLog.outError("BattleGround: Player " I64FMTD " not found!", itr->first); + continue; + } + + if(!plr->isAlive()) + { + plr->ResurrectPlayer(1.0f); + plr->SpawnCorpseBones(); + } + + if(plr->GetTeam() == winner) + { + if(!Source) + Source = plr; + RewardMark(plr,ITEM_WINNER_COUNT); + UpdatePlayerScore(plr, SCORE_BONUS_HONOR, 20); + RewardQuest(plr); + } + else + { + RewardMark(plr,ITEM_LOSER_COUNT); + } + + plr->CombatStopWithPets(true); + + BlockMovement(plr); + + sBattleGroundMgr.BuildPvpLogDataPacket(&data, this); + plr->GetSession()->SendPacket(&data); + + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime()); + plr->GetSession()->SendPacket(&data); + } + + if(Source) + { + ChatHandler(Source).FillMessageData(&data, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, Source->GetGUID(), winmsg); + SendPacketToAll(&data); + } +} + +uint32 BattleGround::GetBattlemasterEntry() const +{ + switch(GetTypeID()) + { + case BATTLEGROUND_AV: return 15972; + case BATTLEGROUND_WS: return 14623; + case BATTLEGROUND_AB: return 14879; + case BATTLEGROUND_EY: return 22516; + case BATTLEGROUND_NA: return 20200; + default: return 0; + } +} + +void BattleGround::RewardMark(Player *plr,uint32 count) +{ + // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens + if(plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE)) + return; + + BattleGroundMarks mark; + bool IsSpell; + switch(GetTypeID()) + { + case BATTLEGROUND_AV: + IsSpell = true; + if(count == ITEM_WINNER_COUNT) + mark = SPELL_AV_MARK_WINNER; + else + mark = SPELL_AV_MARK_LOSER; + break; + case BATTLEGROUND_WS: + IsSpell = true; + if(count == ITEM_WINNER_COUNT) + mark = SPELL_WS_MARK_WINNER; + else + mark = SPELL_WS_MARK_LOSER; + break; + case BATTLEGROUND_AB: + IsSpell = true; + if(count == ITEM_WINNER_COUNT) + mark = SPELL_AB_MARK_WINNER; + else + mark = SPELL_AB_MARK_LOSER; + break; + case BATTLEGROUND_EY: + IsSpell = false; + mark = ITEM_EY_MARK_OF_HONOR; + break; + default: + return; + } + + if(IsSpell) + plr->CastSpell(plr, mark, true); + else if ( objmgr.GetItemPrototype( mark ) ) + { + ItemPosCountVec dest; + uint32 no_space_count = 0; + uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, mark, count, &no_space_count ); + if( msg != EQUIP_ERR_OK ) // convert to possible store amount + count -= no_space_count; + + if( count != 0 && !dest.empty()) // can add some + if(Item* item = plr->StoreNewItem( dest, mark, true, 0)) + plr->SendNewItem(item,count,false,true); + + if(no_space_count > 0) + SendRewardMarkByMail(plr,mark,no_space_count); + } +} + +void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count) +{ + uint32 bmEntry = GetBattlemasterEntry(); + if(!bmEntry) + return; + + ItemPrototype const* markProto = objmgr.GetItemPrototype(mark); + if(!markProto) + return; + + if(Item* markItem = Item::CreateItem(mark,count,plr)) + { + // save new item before send + markItem->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted + + // item + MailItemsInfo mi; + mi.AddItem(markItem->GetGUIDLow(), markItem->GetEntry(), markItem); + + // subject: item name + std::string subject = markProto->Name1; + int loc_idx = plr->GetSession()->GetSessionDbLocaleIndex(); + if ( loc_idx >= 0 ) + if(ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId)) + if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty()) + subject = il->Name[loc_idx]; + + // text + std::string textFormat = plr->GetSession()->GetMangosString(LANG_BG_MARK_BY_MAIL); + char textBuf[300]; + snprintf(textBuf,300,textFormat.c_str(),GetName(),GetName()); + uint32 itemTextId = objmgr.CreateItemText( textBuf ); + + WorldSession::SendMailTo(plr, MAIL_CREATURE, MAIL_STATIONERY_NORMAL, bmEntry, plr->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE); + } +} + +void BattleGround::RewardQuest(Player *plr) +{ + // 'Inactive' this aura prevents the player from gaining honor points and battleground tokens + if(plr->GetDummyAura(SPELL_AURA_PLAYER_INACTIVE)) + return; + + uint32 quest; + switch(GetTypeID()) + { + case BATTLEGROUND_AV: + quest = SPELL_AV_QUEST_REWARD; + break; + case BATTLEGROUND_WS: + quest = SPELL_WS_QUEST_REWARD; + break; + case BATTLEGROUND_AB: + quest = SPELL_AB_QUEST_REWARD; + break; + case BATTLEGROUND_EY: + quest = SPELL_EY_QUEST_REWARD; + break; + default: + return; + } + + plr->CastSpell(plr, quest, true); +} + +void BattleGround::BlockMovement(Player *plr) +{ + plr->SetClientControl(plr, 0); // movement disabled NOTE: the effect will be automatically removed by client when the player is teleported from the battleground, so no need to send with uint8(1) in RemovePlayerAtLeave() +} + +void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket) +{ + // Remove from lists/maps + std::map::iterator itr = m_Players.find(guid); + if(itr != m_Players.end()) + { + UpdatePlayersCountByTeam(itr->second.Team, true); // -1 player + m_Players.erase(itr); + } + + std::map::iterator itr2 = m_PlayerScores.find(guid); + if(itr2 != m_PlayerScores.end()) + { + delete itr2->second; // delete player's score + m_PlayerScores.erase(itr2); + } + + RemovePlayerFromResurrectQueue(guid); + + Player *plr = objmgr.GetPlayer(guid); + + if(plr && !plr->isAlive()) // resurrect on exit + { + plr->ResurrectPlayer(1.0f); + plr->SpawnCorpseBones(); + } + + RemovePlayer(plr, guid); // BG subclass specific code + + if(plr) + { + plr->ClearAfkReports(); + + if(isArena()) + { + if(!sWorld.IsFFAPvPRealm()) + plr->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + } + + WorldPacket data; + if(SendPacket) + { + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetTeam(), plr->GetBattleGroundQueueIndex(m_TypeID), STATUS_NONE, 0, 0); + plr->GetSession()->SendPacket(&data); + } + + // this call is important, because player, when joins to battleground, this method is not called, so it must be called when leaving bg + plr->RemoveBattleGroundQueueId(m_TypeID); + + DecreaseInvitedCount(plr->GetTeam()); + //we should update battleground queue, but only if bg isn't ending + if (GetQueueType() < MAX_BATTLEGROUND_QUEUES) + sBattleGroundMgr.m_BattleGroundQueues[GetTypeID()].Update(GetTypeID(), GetQueueType()); + + if(!plr->GetBattleGroundId()) + return; + + Group * group = plr->GetGroup(); + + // remove from raid group if exist + if(group && group == GetBgRaid(plr->GetTeam())) + { + if(!group->RemoveMember(guid, 0)) // group was disbanded + { + SetBgRaid(plr->GetTeam(), NULL); + delete group; + } + } + + // Do next only if found in battleground + plr->SetBattleGroundId(0); // We're not in BG. + + // Let others know + sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, plr); + SendPacketToTeam(plr->GetTeam(), &data, plr, false); + + if(Transport) + { + plr->TeleportTo(plr->GetBattleGroundEntryPointMap(), plr->GetBattleGroundEntryPointX(), plr->GetBattleGroundEntryPointY(), plr->GetBattleGroundEntryPointZ(), plr->GetBattleGroundEntryPointO()); + //sLog.outDetail("BATTLEGROUND: Sending %s to %f,%f,%f,%f", pl->GetName(), x,y,z,O); + } + + // Log + sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName()); + } + + /// there will be code which will add battleground to BGFreeSlotQueue , when battleground instance will exist + // we always should check if BG is in that queue before adding.. + + if(!GetPlayersSize()) + { + Reset(); + } +} + +// this method is called when no players remains in battleground +void BattleGround::Reset() +{ + SetQueueType(MAX_BATTLEGROUND_QUEUES); + SetWinner(WINNER_NONE); + SetStatus(STATUS_WAIT_QUEUE); + SetStartTime(0); + SetEndTime(0); + SetLastResurrectTime(0); + + m_Events = 0; + + if (m_InvitedAlliance > 0 || m_InvitedHorde > 0) + sLog.outError("BattleGround system ERROR: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde); + + m_InvitedAlliance = 0; + m_InvitedHorde = 0; + + m_Players.clear(); + m_PlayerScores.clear(); + + // reset BGSubclass + this->ResetBGSubclass(); +} + +void BattleGround::StartBattleGround() +{ + ///this method should spawn spirit guides and so on + SetStartTime(0); + + SetLastResurrectTime(0); +} + +void BattleGround::AddPlayer(Player *plr) +{ + // score struct must be created in inherited class + + uint64 guid = plr->GetGUID(); + uint32 team = plr->GetBGTeam(); + + BattleGroundPlayer bp; + bp.LastOnlineTime = 0; + bp.Team = team; + + // Add to list/maps + m_Players[guid] = bp; + + UpdatePlayersCountByTeam(team, false); // +1 player + + WorldPacket data; + sBattleGroundMgr.BuildPlayerJoinedBattleGroundPacket(&data, plr); + SendPacketToTeam(team, &data, plr, false); + + if(isArena()) + { + plr->RemoveArenaSpellCooldowns(); + //plr->RemoveArenaAuras(); + plr->RemoveAllEnchantments(TEMP_ENCHANTMENT_SLOT); + if(team == ALLIANCE && plr->GetTeam() == ALLIANCE) + plr->CastSpell(plr,SPELL_ALLIANCE_GOLD_FLAG,true); + else if(team == HORDE && plr->GetTeam() == ALLIANCE) + plr->CastSpell(plr,SPELL_ALLIANCE_GREEN_FLAG,true); + else if(team == ALLIANCE && plr->GetTeam() == HORDE) + plr->CastSpell(plr,SPELL_HORDE_GOLD_FLAG,true); + else + plr->CastSpell(plr,SPELL_HORDE_GREEN_FLAG,true); + plr->DestroyConjuredItems(true); + + if(GetStatus() == STATUS_WAIT_JOIN) // not started yet + { + plr->CastSpell(plr, SPELL_ARENA_PREPARATION, true); + + plr->SetHealth(plr->GetMaxHealth()); + plr->SetPower(POWER_MANA, plr->GetMaxPower(POWER_MANA)); + } + } + else + { + if(GetStatus() == STATUS_WAIT_JOIN) // not started yet + plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells. + } + + if(isArena()) + plr->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_FFA_PVP); + + // Log + sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName()); +} + +/* This method should be called only once ... it adds pointer to queue */ +void BattleGround::AddToBGFreeSlotQueue() +{ + sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].push_front(this); +} + +/* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/ +void BattleGround::RemoveFromBGFreeSlotQueue() +{ + /* uncomment this code when battlegrounds will work like instances + for (std::deque::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].end(); ++itr) + { + if ((*itr)->GetInstanceID() == m_InstanceID) + { + sBattleGroundMgr.BGFreeSlotQueue[m_TypeID].erase(itr); + return; + } + }*/ +} + +/* +this method should decide, if we can invite new player of certain team to BG, it is based on BATTLEGROUND_STATUS +*/ +bool BattleGround::HasFreeSlotsForTeam(uint32 Team) const +{ + //if BG is starting ... invite anyone: + if (GetStatus() == STATUS_WAIT_JOIN) + return GetInvitedCount(Team) < GetMaxPlayersPerTeam(); + //if BG is already started .. do not allow to join too much players of one faction + uint32 otherTeam; + if (Team == ALLIANCE) + otherTeam = GetInvitedCount(HORDE); + else + otherTeam = GetInvitedCount(ALLIANCE); + if (GetStatus() == STATUS_IN_PROGRESS) + return (GetInvitedCount(Team) <= otherTeam && GetInvitedCount(Team) < GetMaxPlayersPerTeam()); + + return false; +} + +/* this method isn't called already, it will be useful when more battlegrounds of one type will be available */ +bool BattleGround::HasFreeSlots() const +{ + return GetPlayersSize() < GetMaxPlayers(); +} + +void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value) +{ + //this procedure is called from virtual function implemented in bg subclass + std::map::iterator itr = m_PlayerScores.find(Source->GetGUID()); + + if(itr == m_PlayerScores.end()) // player not found... + return; + + switch(type) + { + case SCORE_KILLING_BLOWS: // Killing blows + itr->second->KillingBlows += value; + break; + case SCORE_DEATHS: // Deaths + itr->second->Deaths += value; + break; + case SCORE_HONORABLE_KILLS: // Honorable kills + itr->second->HonorableKills += value; + break; + case SCORE_BONUS_HONOR: // Honor bonus + // reward honor instantly + if(Source->RewardHonor(NULL, 1, value)) + itr->second->BonusHonor += value; + break; + //used only in EY, but in MSG_PVP_LOG_DATA opcode + case SCORE_DAMAGE_DONE: // Damage Done + itr->second->DamageDone += value; + break; + case SCORE_HEALING_DONE: // Healing Done + itr->second->HealingDone += value; + break; + default: + sLog.outError("BattleGround: Unknown player score type %u", type); + break; + } +} + +void BattleGround::AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid) +{ + m_ReviveQueue[npc_guid].push_back(player_guid); + + Player *plr = objmgr.GetPlayer(player_guid); + if(!plr) + return; + + plr->CastSpell(plr, SPELL_WAITING_FOR_RESURRECT, true); + SpellEntry const *spellInfo = sSpellStore.LookupEntry( SPELL_WAITING_FOR_RESURRECT ); + if(spellInfo) + { + Aura *Aur = CreateAura(spellInfo, 0, NULL, plr); + plr->AddAura(Aur); + } +} + +void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid) +{ + for(std::map >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr) + { + for(std::vector::iterator itr2 =(itr->second).begin(); itr2 != (itr->second).end(); ++itr2) + { + if(*itr2 == player_guid) + { + (itr->second).erase(itr2); + + Player *plr = objmgr.GetPlayer(player_guid); + if(!plr) + return; + + plr->RemoveAurasDueToSpell(SPELL_WAITING_FOR_RESURRECT); + + return; + } + } + } +} + +bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime) +{ + GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry); + if(!goinfo) + { + sLog.outErrorDb("Gameobject template %u not found in database! BattleGround not created!", entry); + return false; + } + + uint32 guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT); + + GameObjectData& data = objmgr.NewGOData(guid); + + data.id = entry; + data.mapid = GetMapId(); + data.posX = x; + data.posY = y; + data.posZ = z; + data.orientation = o; + data.rotation0 = rotation0; + data.rotation1 = rotation1; + data.rotation2 = rotation2; + data.rotation3 = rotation3; + data.spawntimesecs = respawnTime; + data.animprogress = 100; + data.go_state = 1; + data.spawnMask = 1; + objmgr.AddGameobjectToGrid(guid, &data); + + m_BgObjects[type] = MAKE_NEW_GUID(guid, entry, HIGHGUID_GAMEOBJECT); + + return true; +} + +//some doors aren't despawned so we cannot handle their closing in gameobject::update() +//it would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code +void BattleGround::DoorClose(uint32 type) +{ + GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + if(obj) + { + //if doors are open, close it + if( obj->getLootState() == GO_ACTIVATED && !obj->GetGoState() ) + { + //change state to allow door to be closed + obj->SetLootState(GO_READY); + obj->UseDoorOrButton(RESPAWN_ONE_DAY); + } + } + else + { + sLog.outError("BattleGround: Door object not found (cannot close doors)"); + } +} + +void BattleGround::DoorOpen(uint32 type) +{ + GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + if(obj) + { + //change state to be sure they will be opened + obj->SetLootState(GO_READY); + obj->UseDoorOrButton(RESPAWN_ONE_DAY); + } + else + { + sLog.outError("BattleGround: Door object not found! - doors will be closed."); + } +} + +void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime) +{ + if( respawntime == 0 ) + { + GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + if(obj) + { + //we need to change state from GO_JUST_DEACTIVATED to GO_READY in case battleground is starting again + if( obj->getLootState() == GO_JUST_DEACTIVATED ) + obj->SetLootState(GO_READY); + obj->Respawn(); + } + else + objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, 0); + } + else + { + GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + if(obj) + { + obj->SetRespawnTime(respawntime); + obj->SetLootState(GO_JUST_DEACTIVATED); + } + else + objmgr.SaveGORespawnTime(GUID_LOPART(m_BgObjects[type]), 0, time(NULL) + respawntime); + } +} + +Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o) +{ + // note: this should normally be FindMap + // but it's a hack to allow the battlegrounds to initialize at server startup + Map * map = MapManager::Instance().GetMap(GetMapId(), 0); + if(!map) return NULL; + + Creature* pCreature = new Creature; + if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, entry, teamval)) + { + sLog.outError("Can't create creature entry: %u",entry); + delete pCreature; + return NULL; + } + + pCreature->Relocate(x, y, z, o); + + if(!pCreature->IsPositionValid()) + { + sLog.outError("ERROR: Creature (guidlow %d, entry %d) not added to battleground. Suggested coordinates isn't valid (X: %f Y: %f)",pCreature->GetGUIDLow(),pCreature->GetEntry(),pCreature->GetPositionX(),pCreature->GetPositionY()); + return NULL; + } + + pCreature->AIM_Initialize(); + + //pCreature->SetDungeonDifficulty(0); + + map->Add(pCreature); + m_BgCreatures[type] = pCreature->GetGUID(); + return pCreature; +} + +bool BattleGround::DelCreature(uint32 type) +{ + Creature *cr = HashMapHolder::Find(m_BgCreatures[type]); + if(!cr) + { + sLog.outError("Can't find creature guid: %u",m_BgCreatures[type]); + return false; + } + cr->CleanupsBeforeDelete(); + cr->AddObjectToRemoveList(); + m_BgCreatures[type] = 0; + return true; +} + +bool BattleGround::DelObject(uint32 type) +{ + GameObject *obj = HashMapHolder::Find(m_BgObjects[type]); + if(!obj) + { + sLog.outError("Can't find gobject guid: %u",m_BgObjects[type]); + return false; + } + obj->SetRespawnTime(0); // not save respawn time + obj->Delete(); + m_BgObjects[type] = 0; + return true; +} + +bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team) +{ + uint32 entry = 0; + + if(team == ALLIANCE) + entry = 13116; + else + entry = 13117; + + Creature* pCreature = AddCreature(entry,type,team,x,y,z,o); + if(!pCreature) + { + sLog.outError("Can't create Spirit guide. BattleGround not created!"); + this->EndNow(); + return false; + } + + pCreature->setDeathState(DEAD); + + pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID()); + // aura + pCreature->SetUInt32Value(UNIT_FIELD_AURA, SPELL_SPIRIT_HEAL_CHANNEL); + pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009); + pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C); + pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF); + // casting visual effect + pCreature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SPIRIT_HEAL_CHANNEL); + // correct cast speed + pCreature->SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); + + //pCreature->CastSpell(pCreature, SPELL_SPIRIT_HEAL_CHANNEL, true); + + return true; +} + +void BattleGround::SendMessageToAll(char const* text) +{ + WorldPacket data; + ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, text, NULL); + SendPacketToAll(&data); +} + +void BattleGround::SendMessageToAll(int32 entry) +{ + char const* text = GetMangosString(entry); + WorldPacket data; + ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_BG_SYSTEM_NEUTRAL, LANG_UNIVERSAL, NULL, 0, text, NULL); + SendPacketToAll(&data); +} + +void BattleGround::EndNow() +{ + SetStatus(STATUS_WAIT_LEAVE); + SetEndTime(TIME_TO_AUTOREMOVE); +} + +// Battleground messages are localized using the dbc lang, they are not client language dependent +const char *BattleGround::GetMangosString(int32 entry) +{ + // FIXME: now we have different DBC locales and need localized message for each target client + return objmgr.GetMangosStringForDBCLocale(entry); +} + +/* +important notice: +buffs aren't spawned/despawned when players captures anything +buffs are in their positions when battleground starts +*/ +void BattleGround::HandleTriggerBuff(uint64 const& go_guid) +{ + GameObject *obj = HashMapHolder::Find(go_guid); + if(!obj || obj->GetGoType() != GAMEOBJECT_TYPE_TRAP || !obj->isSpawned()) + return; + + //change buff type, when buff is used: + int32 index = m_BgObjects.size() - 1; + while (index >= 0 && m_BgObjects[index] != go_guid) + index--; + if (index < 0) + { + sLog.outError("BattleGround (Type: %u) has buff gameobject (Guid: %u Entry: %u Type:%u) but it hasn't that object in its internal data",GetTypeID(),GUID_LOPART(go_guid),obj->GetEntry(),obj->GetGoType()); + return; + } + + //randomly select new buff + uint8 buff = urand(0, 2); + uint32 entry = obj->GetEntry(); + if( m_BuffChange && entry != Buff_Entries[buff] ) + { + //despawn current buff + SpawnBGObject(index, RESPAWN_ONE_DAY); + //set index for new one + for (uint8 currBuffTypeIndex = 0; currBuffTypeIndex < 3; ++currBuffTypeIndex) + if( entry == Buff_Entries[currBuffTypeIndex] ) + { + index -= currBuffTypeIndex; + index += buff; + } + } + + SpawnBGObject(index, BUFF_RESPAWN_TIME); +} + +void BattleGround::HandleKillPlayer( Player *player, Player *killer ) +{ + //keep in mind that for arena this will have to be changed a bit + + // add +1 deaths + UpdatePlayerScore(player, SCORE_DEATHS, 1); + + // add +1 kills to group and +1 killing_blows to killer + if( killer ) + { + UpdatePlayerScore(killer, SCORE_HONORABLE_KILLS, 1); + UpdatePlayerScore(killer, SCORE_KILLING_BLOWS, 1); + + for(std::map::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + Player *plr = objmgr.GetPlayer(itr->first); + + if(!plr || plr == killer) + continue; + + if( plr->GetTeam() == killer->GetTeam() && plr->IsAtGroupRewardDistance(player) ) + UpdatePlayerScore(plr, SCORE_HONORABLE_KILLS, 1); + } + } + + // to be able to remove insignia + player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE ); +} diff --git a/src/game/BattleGround.h b/src/game/BattleGround.h new file mode 100644 index 00000000000..154e5b02416 --- /dev/null +++ b/src/game/BattleGround.h @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUND_H +#define __BATTLEGROUND_H + +#include "Common.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Opcodes.h" +#include "ObjectMgr.h" +#include "BattleGroundMgr.h" +#include "SharedDefines.h" + +enum BattleGroundSounds +{ + SOUND_HORDE_WINS = 8454, + SOUND_ALLIANCE_WINS = 8455, + SOUND_BG_START = 3439 +}; + +enum BattleGroundQuests +{ + SPELL_WS_QUEST_REWARD = 43483, + SPELL_AB_QUEST_REWARD = 43484, + SPELL_AV_QUEST_REWARD = 43475, + SPELL_AV_QUEST_KILLED_BOSS = 23658, + SPELL_EY_QUEST_REWARD = 43477, + SPELL_AB_QUEST_REWARD_4_BASES = 24061, + SPELL_AB_QUEST_REWARD_5_BASES = 24064 +}; + +enum BattleGroundMarks +{ + SPELL_WS_MARK_LOSER = 24950, + SPELL_WS_MARK_WINNER = 24951, + SPELL_AB_MARK_LOSER = 24952, + SPELL_AB_MARK_WINNER = 24953, + SPELL_AV_MARK_LOSER = 24954, + SPELL_AV_MARK_WINNER = 24955, + ITEM_EY_MARK_OF_HONOR = 29024 +}; + +enum BattleGroundMarksCount +{ + ITEM_WINNER_COUNT = 3, + ITEM_LOSER_COUNT = 1 +}; + +enum BattleGroundSpells +{ + SPELL_WAITING_FOR_RESURRECT = 2584, // Waiting to Resurrect + SPELL_SPIRIT_HEAL_CHANNEL = 22011, // Spirit Heal Channel + SPELL_SPIRIT_HEAL = 22012, // Spirit Heal + SPELL_RESURRECTION_VISUAL = 24171, // Resurrection Impact Visual + SPELL_ARENA_PREPARATION = 32727, // use this one, 32728 not correct + SPELL_ALLIANCE_GOLD_FLAG = 32724, + SPELL_ALLIANCE_GREEN_FLAG = 32725, + SPELL_HORDE_GOLD_FLAG = 35774, + SPELL_HORDE_GREEN_FLAG = 35775, + SPELL_PREPARATION = 44521, // Preparation + SPELL_SPIRIT_HEAL_MANA = 44535, // Spirit Heal + SPELL_RECENTLY_DROPPED_FLAG = 42792, // Recently Dropped Flag + SPELL_AURA_PLAYER_INACTIVE = 43681 // Inactive +}; + +enum BattleGroundTimeIntervals +{ + RESURRECTION_INTERVAL = 30000, // ms + REMIND_INTERVAL = 30000, // ms + INVITE_ACCEPT_WAIT_TIME = 120000, // ms + TIME_TO_AUTOREMOVE = 120000, // ms + MAX_OFFLINE_TIME = 300000, // ms + START_DELAY0 = 120000, // ms + START_DELAY1 = 60000, // ms + START_DELAY2 = 30000, // ms + START_DELAY3 = 15000, // ms used only in arena + RESPAWN_ONE_DAY = 86400, // secs + RESPAWN_IMMEDIATELY = 0, // secs + BUFF_RESPAWN_TIME = 180, // secs + BG_HONOR_SCORE_TICKS = 330 // points +}; + +enum BattleGroundBuffObjects +{ + BG_OBJECTID_SPEEDBUFF_ENTRY = 179871, + BG_OBJECTID_REGENBUFF_ENTRY = 179904, + BG_OBJECTID_BERSERKERBUFF_ENTRY = 179905 +}; + +const uint32 Buff_Entries[3] = { BG_OBJECTID_SPEEDBUFF_ENTRY, BG_OBJECTID_REGENBUFF_ENTRY, BG_OBJECTID_BERSERKERBUFF_ENTRY }; + +enum BattleGroundStatus +{ + STATUS_NONE = 0, + STATUS_WAIT_QUEUE = 1, + STATUS_WAIT_JOIN = 2, + STATUS_IN_PROGRESS = 3, + STATUS_WAIT_LEAVE = 4 // custom +}; + +struct BattleGroundPlayer +{ + uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes + uint32 Team; // Player's team +}; + +struct BattleGroundObjectInfo +{ + BattleGroundObjectInfo() : object(NULL), timer(0), spellid(0) {} + + GameObject *object; + int32 timer; + uint32 spellid; +}; + +#define MAX_QUEUED_PLAYERS_MAP 7 + +enum BattleGroundTypeId +{ + BATTLEGROUND_AV = 1, + BATTLEGROUND_WS = 2, + BATTLEGROUND_AB = 3, + BATTLEGROUND_NA = 4, + BATTLEGROUND_BE = 5, + BATTLEGROUND_AA = 6, + BATTLEGROUND_EY = 7, + BATTLEGROUND_RL = 8 +}; + +enum ScoreType +{ + SCORE_KILLING_BLOWS = 1, + SCORE_DEATHS = 2, + SCORE_HONORABLE_KILLS = 3, + SCORE_BONUS_HONOR = 4, + //EY, but in MSG_PVP_LOG_DATA opcode! + SCORE_DAMAGE_DONE = 5, + SCORE_HEALING_DONE = 6, + //WS + SCORE_FLAG_CAPTURES = 7, + SCORE_FLAG_RETURNS = 8, + //AB + SCORE_BASES_ASSAULTED = 9, + SCORE_BASES_DEFENDED = 10, + //AV + SCORE_GRAVEYARDS_ASSAULTED = 11, + SCORE_GRAVEYARDS_DEFENDED = 12, + SCORE_TOWERS_ASSAULTED = 13, + SCORE_TOWERS_DEFENDED = 14, + SCORE_MINES_CAPTURED = 15, + SCORE_LEADERS_KILLED = 16, + SCORE_SECONDARY_OBJECTIVES = 17 + // TODO : implement them +}; + +enum ArenaType +{ + ARENA_TYPE_2v2 = 2, + ARENA_TYPE_3v3 = 3, + ARENA_TYPE_5v5 = 5 +}; + +enum BattleGroundType +{ + TYPE_BATTLEGROUND = 3, + TYPE_ARENA = 4 +}; + +enum BattleGroundWinner +{ + WINNER_HORDE = 0, + WINNER_ALLIANCE = 1, + WINNER_NONE = 2 +}; + +enum BattleGroundTeamId +{ + BG_TEAM_ALLIANCE = 0, + BG_TEAM_HORDE = 1 +}; + +class BattleGroundScore +{ + public: + BattleGroundScore() : KillingBlows(0), HonorableKills(0), Deaths(0), DamageDone(0), HealingDone(0), BonusHonor(0) {}; + virtual ~BattleGroundScore() //virtual destructor is used when deleting score from scores map + { + }; + uint32 KillingBlows; + uint32 Deaths; + uint32 HonorableKills; + uint32 BonusHonor; + uint32 DamageDone; + uint32 HealingDone; +}; + +/* +This class is used to: +1. Add player to battleground +2. Remove player from battleground +3. some certain cases, same for all battlegrounds +4. It has properties same for all battlegrounds +*/ +class BattleGround +{ + friend class BattleGroundMgr; + + public: + /* Construction */ + BattleGround(); + virtual ~BattleGround(); + virtual void Update(time_t diff); // must be implemented in BG subclass of BG specific update code, but must in begginning call parent version + virtual bool SetupBattleGround() // must be implemented in BG subclass + { + return true; + } + void Reset(); // resets all common properties for battlegrounds + virtual void ResetBGSubclass() // must be implemented in BG subclass + { + } + + /* Battleground */ + // Get methods: + char const* GetName() const { return m_Name; } + uint32 GetTypeID() const { return m_TypeID; } + uint32 GetQueueType() const { return m_Queue_type; } + uint32 GetInstanceID() const { return m_InstanceID; } + uint32 GetStatus() const { return m_Status; } + uint32 GetStartTime() const { return m_StartTime; } + uint32 GetEndTime() const { return m_EndTime; } + uint32 GetLastResurrectTime() const { return m_LastResurrectTime; } + uint32 GetMaxPlayers() const { return m_MaxPlayers; } + uint32 GetMinPlayers() const { return m_MinPlayers; } + + uint32 GetMinLevel() const { return m_LevelMin; } + uint32 GetMaxLevel() const { return m_LevelMax; } + + uint32 GetMaxPlayersPerTeam() const { return m_MaxPlayersPerTeam; } + uint32 GetMinPlayersPerTeam() const { return m_MinPlayersPerTeam; } + + int GetStartDelayTime() const { return m_StartDelayTime; } + uint8 GetArenaType() const { return m_ArenaType; } + uint8 GetWinner() const { return m_Winner; } + uint32 GetBattlemasterEntry() const; + + // Set methods: + void SetName(char const* Name) { m_Name = Name; } + void SetTypeID(uint32 TypeID) { m_TypeID = TypeID; } + void SetQueueType(uint32 ID) { m_Queue_type = ID; } + void SetInstanceID(uint32 InstanceID) { m_InstanceID = InstanceID; } + void SetStatus(uint32 Status) { m_Status = Status; } + void SetStartTime(uint32 Time) { m_StartTime = Time; } + void SetEndTime(uint32 Time) { m_EndTime = Time; } + void SetLastResurrectTime(uint32 Time) { m_LastResurrectTime = Time; } + void SetMaxPlayers(uint32 MaxPlayers) { m_MaxPlayers = MaxPlayers; } + void SetMinPlayers(uint32 MinPlayers) { m_MinPlayers = MinPlayers; } + void SetLevelRange(uint32 min, uint32 max) { m_LevelMin = min; m_LevelMax = max; } + void SetRated(bool state) { m_IsRated = state; } + void SetArenaType(uint8 type) { m_ArenaType = type; } + void SetArenaorBGType(bool _isArena) { m_IsArena = _isArena; } + void SetWinner(uint8 winner) { m_Winner = winner; } + + void ModifyStartDelayTime(int diff) { m_StartDelayTime -= diff; } + void SetStartDelayTime(int Time) { m_StartDelayTime = Time; } + + void SetMaxPlayersPerTeam(uint32 MaxPlayers) { m_MaxPlayersPerTeam = MaxPlayers; } + void SetMinPlayersPerTeam(uint32 MinPlayers) { m_MinPlayersPerTeam = MinPlayers; } + + void AddToBGFreeSlotQueue(); //this queue will be useful when more battlegrounds instances will be available + void RemoveFromBGFreeSlotQueue(); //this method could delete whole BG instance, if another free is available + + void DecreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? --m_InvitedAlliance : --m_InvitedHorde; } + void IncreaseInvitedCount(uint32 team) { (team == ALLIANCE) ? ++m_InvitedAlliance : ++m_InvitedHorde; } + uint32 GetInvitedCount(uint32 team) const + { + if( team == ALLIANCE ) + return m_InvitedAlliance; + else + return m_InvitedHorde; + } + bool HasFreeSlotsForTeam(uint32 Team) const; + bool HasFreeSlots() const; + + bool isArena() const { return m_IsArena; } + bool isBattleGround() const { return !m_IsArena; } + bool isRated() const { return m_IsRated; } + + typedef std::map BattleGroundPlayerMap; + BattleGroundPlayerMap const& GetPlayers() const { return m_Players; } + uint32 GetPlayersSize() const { return m_Players.size(); } + uint32 GetRemovedPlayersSize() const { return m_RemovedPlayers.size(); } + + std::map::const_iterator GetPlayerScoresBegin() const { return m_PlayerScores.begin(); } + std::map::const_iterator GetPlayerScoresEnd() const { return m_PlayerScores.end(); } + uint32 GetPlayerScoresSize() const { return m_PlayerScores.size(); } + + uint32 GetReviveQueueSize() const { return m_ReviveQueue.size(); } + + void AddPlayerToResurrectQueue(uint64 npc_guid, uint64 player_guid); + void RemovePlayerFromResurrectQueue(uint64 player_guid); + + void StartBattleGround(); + + /* Location */ + void SetMapId(uint32 MapID) { m_MapId = MapID; } + uint32 GetMapId() const { return m_MapId; } + + void SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O); + void GetTeamStartLoc(uint32 TeamID, float &X, float &Y, float &Z, float &O) const + { + uint8 idx = GetTeamIndexByTeamId(TeamID); + X = m_TeamStartLocX[idx]; + Y = m_TeamStartLocY[idx]; + Z = m_TeamStartLocZ[idx]; + O = m_TeamStartLocO[idx]; + } + + /* Packet Transfer */ + // method that should fill worldpacket with actual world states (not yet implemented for all battlegrounds!) + virtual void FillInitialWorldStates(WorldPacket& /*data*/) {} + void SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender = NULL, bool self = true); + void SendPacketToAll(WorldPacket *packet); + void PlaySoundToTeam(uint32 SoundID, uint32 TeamID); + void PlaySoundToAll(uint32 SoundID); + void CastSpellOnTeam(uint32 SpellID, uint32 TeamID); + void RewardHonorToTeam(uint32 Honor, uint32 TeamID); + void RewardReputationToTeam(uint32 faction_id, uint32 Reputation, uint32 TeamID); + void RewardMark(Player *plr,uint32 count); + void SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count); + void RewardQuest(Player *plr); + void UpdateWorldState(uint32 Field, uint32 Value); + void UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source); + void EndBattleGround(uint32 winner); + void BlockMovement(Player *plr); + + void SendMessageToAll(char const* text); + void SendMessageToAll(int32 entry); + + /* Raid Group */ + Group *GetBgRaid(uint32 TeamID) const { return TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE]; } + void SetBgRaid(uint32 TeamID, Group *bg_raid) + { + Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE]; + if(old_raid) old_raid->SetBattlegroundGroup(NULL); + if(bg_raid) bg_raid->SetBattlegroundGroup(this); + old_raid = bg_raid; + } + + virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); + + uint8 GetTeamIndexByTeamId(uint32 Team) const { return Team == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; } + uint32 GetPlayersCountByTeam(uint32 Team) const { return m_PlayersCount[GetTeamIndexByTeamId(Team)]; } + void UpdatePlayersCountByTeam(uint32 Team, bool remove) + { + if(remove) + --m_PlayersCount[GetTeamIndexByTeamId(Team)]; + else + ++m_PlayersCount[GetTeamIndexByTeamId(Team)]; + } + + /* Triggers handle */ + // must be implemented in BG subclass + virtual void HandleAreaTrigger(Player* /*Source*/, uint32 /*Trigger*/) {} + // must be implemented in BG subclass if need AND call base class generic code + virtual void HandleKillPlayer(Player *player, Player *killer); + + /* Battleground events */ + /* these functions will return true event is possible, but false if player is bugger */ + virtual void EventPlayerDroppedFlag(Player* /*player*/) {} + virtual void EventPlayerClickedOnFlag(Player* /*player*/, GameObject* /*target_obj*/) {} + virtual void EventPlayerCapturedFlag(Player* /*player*/) {} + + /* Death related */ + virtual WorldSafeLocsEntry const* GetClosestGraveYard(float /*x*/, float /*y*/, float /*z*/, uint32 /*team*/) { return NULL; } + + virtual void AddPlayer(Player *plr); // must be implemented in BG subclass + virtual void RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket); + // can be extended in in BG subclass + + void HandleTriggerBuff(uint64 const& go_guid); + + // TODO: make this protected: + typedef std::vector BGObjects; + typedef std::vector BGCreatures; + BGObjects m_BgObjects; + BGCreatures m_BgCreatures; + void SpawnBGObject(uint32 type, uint32 respawntime); + bool AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime = 0); + Creature* AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o); + bool DelCreature(uint32 type); + bool DelObject(uint32 type); + bool AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team); + + void DoorOpen(uint32 type); + void DoorClose(uint32 type); + const char *GetMangosString(int32 entry); + + protected: + //this method is called, when BG cannot spawn its own spirit guide, or something is wrong, It correctly ends BattleGround + void EndNow(); + + /* Scorekeeping */ + // Player scores + std::map m_PlayerScores; + // must be implemented in BG subclass + virtual void RemovePlayer(Player * /*player*/, uint64 /*guid*/) {} + + /* Player lists, those need to be accessible by inherited classes */ + BattleGroundPlayerMap m_Players; + // Spirit Guide guid + Player list GUIDS + std::map > m_ReviveQueue; + + /* + this is important variable used for invitation messages + */ + uint8 m_Events; + + bool m_BuffChange; + + private: + /* Battleground */ + uint32 m_TypeID; //Battleground type, defined in enum BattleGroundTypeId + uint32 m_InstanceID; //BattleGround Instance's GUID! + uint32 m_Status; + uint32 m_StartTime; + uint32 m_EndTime; + uint32 m_LastResurrectTime; + uint32 m_Queue_type; + uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5 + // this variable is not used .... it can be found in many other ways... but to store it in BG object instance is useless + //uint8 m_BattleGroundType; // 3=BG, 4=arena + //instead of uint8 (in previous line) is bool used + bool m_IsArena; + uint8 m_Winner; // 0=alliance, 1=horde, 2=none + int32 m_StartDelayTime; + bool m_IsRated; // is this battle rated? + char const *m_Name; + + /* Player lists */ + std::vector m_ResurrectQueue; // Player GUID + std::map m_RemovedPlayers; // uint8 is remove type (0 - bgqueue, 1 - bg, 2 - resurrect queue) + + /* Invited counters are useful for player invitation to BG - do not allow, if BG is started to one faction to have 2 more players than another faction */ + /* Invited counters will be changed only when removing already invited player from queue, removing player from battleground and inviting player to BG */ + /* Invited players counters*/ + uint32 m_InvitedAlliance; + uint32 m_InvitedHorde; + + /* Raid Group */ + Group *m_BgRaids[2]; // 0 - alliance, 1 - horde + + /* Players count by team */ + uint32 m_PlayersCount[2]; + + /* Limits */ + uint32 m_LevelMin; + uint32 m_LevelMax; + uint32 m_MaxPlayersPerTeam; + uint32 m_MaxPlayers; + uint32 m_MinPlayersPerTeam; + uint32 m_MinPlayers; + + /* Location */ + uint32 m_MapId; + float m_TeamStartLocX[2]; + float m_TeamStartLocY[2]; + float m_TeamStartLocZ[2]; + float m_TeamStartLocO[2]; +}; +#endif diff --git a/src/game/BattleGroundAA.cpp b/src/game/BattleGroundAA.cpp new file mode 100644 index 00000000000..6929ef29681 --- /dev/null +++ b/src/game/BattleGroundAA.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundAA.h" + +BattleGroundAA::BattleGroundAA() +{ + +} + +BattleGroundAA::~BattleGroundAA() +{ + +} + +void BattleGroundAA::Update(time_t diff) +{ + BattleGround::Update(diff); +} + +void BattleGroundAA::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundAAScore* sc = new BattleGroundAAScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundAA::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) +{ +} + +void BattleGroundAA::HandleKillPlayer(Player* player, Player* killer) +{ + BattleGround::HandleKillPlayer(player, killer); +} + +void BattleGroundAA::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/) +{ +} + +bool BattleGroundAA::SetupBattleGround() +{ + return true; +} diff --git a/src/game/BattleGroundAA.h b/src/game/BattleGroundAA.h new file mode 100644 index 00000000000..3db494430ee --- /dev/null +++ b/src/game/BattleGroundAA.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDAA_H +#define __BATTLEGROUNDAA_H + +class BattleGround; + +class BattleGroundAAScore : public BattleGroundScore +{ + public: + BattleGroundAAScore() {}; + virtual ~BattleGroundAAScore() {}; + //TODO fix me +}; + +class BattleGroundAA : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundAA(); + ~BattleGroundAA(); + void Update(time_t diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + void HandleKillPlayer(Player* player, Player *killer); +}; +#endif diff --git a/src/game/BattleGroundAB.cpp b/src/game/BattleGroundAB.cpp new file mode 100644 index 00000000000..feb305e546a --- /dev/null +++ b/src/game/BattleGroundAB.cpp @@ -0,0 +1,649 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Object.h" +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundAB.h" +#include "Creature.h" +#include "Chat.h" +#include "ObjectMgr.h" +#include "MapManager.h" +#include "Language.h" +#include "Util.h" + +BattleGroundAB::BattleGroundAB() +{ + m_BuffChange = true; + m_BgObjects.resize(BG_AB_OBJECT_MAX); + m_BgCreatures.resize(BG_AB_ALL_NODES_COUNT); +} + +BattleGroundAB::~BattleGroundAB() +{ +} + +void BattleGroundAB::Update(time_t diff) +{ + BattleGround::Update(diff); + + if( GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize() ) + { + ModifyStartDelayTime(diff); + + if( !(m_Events & 0x01) ) + { + m_Events |= 0x01; + + sLog.outDebug("Arathi Basin: entering state STATUS_WAIT_JOIN ..."); + + // despawn banners, auras and buffs + for (int obj = BG_AB_OBJECT_BANNER_NEUTRAL; obj < BG_AB_DYNAMIC_NODES_COUNT * 8; ++obj) + SpawnBGObject(obj, RESPAWN_ONE_DAY); + for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT * 3; ++i) + SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + i, RESPAWN_ONE_DAY); + + // Starting doors + SpawnBGObject(BG_AB_OBJECT_GATE_A, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_AB_OBJECT_GATE_H, RESPAWN_IMMEDIATELY); + DoorClose(BG_AB_OBJECT_GATE_A); + DoorClose(BG_AB_OBJECT_GATE_H); + + // Starting base spirit guides + _NodeOccupied(BG_AB_SPIRIT_ALIANCE,ALLIANCE); + _NodeOccupied(BG_AB_SPIRIT_HORDE,HORDE); + + SetStartDelayTime(START_DELAY0); + } + // After 1 minute, warning is signalled + else if( GetStartDelayTime() <= START_DELAY1 && !(m_Events & 0x04) ) + { + m_Events |= 0x04; + SendMessageToAll(GetMangosString(LANG_BG_AB_ONEMINTOSTART)); + } + // After 1,5 minute, warning is signalled + else if( GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x08) ) + { + m_Events |= 0x08; + SendMessageToAll(GetMangosString(LANG_BG_AB_HALFMINTOSTART)); + } + // After 2 minutes, gates OPEN ! x) + else if( GetStartDelayTime() < 0 && !(m_Events & 0x10) ) + { + m_Events |= 0x10; + SendMessageToAll(GetMangosString(LANG_BG_AB_STARTED)); + + // spawn neutral banners + for (int banner = BG_AB_OBJECT_BANNER_NEUTRAL, i = 0; i < 5; banner += 8, ++i) + SpawnBGObject(banner, RESPAWN_IMMEDIATELY); + for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + //randomly select buff to spawn + uint8 buff = urand(0, 2); + SpawnBGObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + buff + i * 3, RESPAWN_IMMEDIATELY); + } + DoorOpen(BG_AB_OBJECT_GATE_A); + DoorOpen(BG_AB_OBJECT_GATE_H); + + PlaySoundToAll(SOUND_BG_START); + SetStatus(STATUS_IN_PROGRESS); + + for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if(Player* plr = objmgr.GetPlayer(itr->first)) + plr->RemoveAurasDueToSpell(SPELL_PREPARATION); + } + + } + else if( GetStatus() == STATUS_IN_PROGRESS ) + { + int team_points[2] = { 0, 0 }; + + for (int node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) + { + // 3 sec delay to spawn new banner instead previous despawned one + if( m_BannerTimers[node].timer ) + { + if( m_BannerTimers[node].timer > diff ) + m_BannerTimers[node].timer -= diff; + else + { + m_BannerTimers[node].timer = 0; + _CreateBanner(node, m_BannerTimers[node].type, m_BannerTimers[node].teamIndex, false); + } + } + + // 1-minute to occupy a node from contested state + if( m_NodeTimers[node] ) + { + if( m_NodeTimers[node] > diff ) + m_NodeTimers[node] -= diff; + else + { + m_NodeTimers[node] = 0; + // Change from contested to occupied ! + uint8 teamIndex = m_Nodes[node]-1; + m_prevNodes[node] = m_Nodes[node]; + m_Nodes[node] += 2; + // burn current contested banner + _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex); + // create new occupied banner + _CreateBanner(node, BG_AB_NODE_TYPE_OCCUPIED, teamIndex, true); + _SendNodeUpdate(node); + _NodeOccupied(node,(teamIndex == 0) ? ALLIANCE:HORDE); + // Message to chatlog + char buf[256]; + uint8 type = (teamIndex == 0) ? CHAT_MSG_BG_SYSTEM_ALLIANCE : CHAT_MSG_BG_SYSTEM_HORDE; + sprintf(buf, GetMangosString(LANG_BG_AB_NODE_TAKEN), (teamIndex == 0) ? GetMangosString(LANG_BG_AB_ALLY) : GetMangosString(LANG_BG_AB_HORDE), _GetNodeName(node)); + WorldPacket data; + ChatHandler::FillMessageData(&data, NULL, type, LANG_UNIVERSAL, NULL, 0, buf, NULL); + SendPacketToAll(&data); + PlaySoundToAll((teamIndex == 0) ? SOUND_NODE_CAPTURED_ALLIANCE : SOUND_NODE_CAPTURED_HORDE); + } + } + + for (int team = 0; team < 2; ++team) + if( m_Nodes[node] == team + BG_AB_NODE_TYPE_OCCUPIED ) + ++team_points[team]; + } + + // Accumulate points + for (int team = 0; team < 2; ++team) + { + int points = team_points[team]; + if( !points ) + continue; + m_lastTick[team] += diff; + if( m_lastTick[team] > BG_AB_TickIntervals[points] ) + { + m_lastTick[team] -= BG_AB_TickIntervals[points]; + m_TeamScores[team] += BG_AB_TickPoints[points]; + m_HonorScoreTics[team] += BG_AB_TickPoints[points]; + m_ReputationScoreTics[team] += BG_AB_TickPoints[points]; + if( m_ReputationScoreTics[team] >= 200 ) + { + (team == BG_TEAM_ALLIANCE) ? RewardReputationToTeam(509, 10, ALLIANCE) : RewardReputationToTeam(510, 10, HORDE); + m_ReputationScoreTics[team] -= 200; + } + if( m_HonorScoreTics[team] >= BG_HONOR_SCORE_TICKS ) + { + (team == BG_TEAM_ALLIANCE) ? RewardHonorToTeam(20, ALLIANCE) : RewardHonorToTeam(20, HORDE); + m_HonorScoreTics[team] -= BG_HONOR_SCORE_TICKS; + } + if( !m_IsInformedNearVictory && m_TeamScores[team] > 1800 ) + { + if( team == BG_TEAM_ALLIANCE ) + SendMessageToAll(GetMangosString(LANG_BG_AB_A_NEAR_VICTORY)); + else + SendMessageToAll(GetMangosString(LANG_BG_AB_H_NEAR_VICTORY)); + PlaySoundToAll(SOUND_NEAR_VICTORY); + m_IsInformedNearVictory = true; + } + + if( m_TeamScores[team] > 2000 ) + m_TeamScores[team] = 2000; + if( team == BG_TEAM_ALLIANCE ) + UpdateWorldState(BG_AB_OP_RESOURCES_ALLY, m_TeamScores[team]); + if( team == BG_TEAM_HORDE ) + UpdateWorldState(BG_AB_OP_RESOURCES_HORDE, m_TeamScores[team]); + } + } + + // Test win condition + if( m_TeamScores[BG_TEAM_ALLIANCE] >= 2000 ) + EndBattleGround(ALLIANCE); + if( m_TeamScores[BG_TEAM_HORDE] >= 2000 ) + EndBattleGround(HORDE); + } +} + +void BattleGroundAB::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in the constructor + BattleGroundABScore* sc = new BattleGroundABScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundAB::RemovePlayer(Player * /*plr*/, uint64 /*guid*/) +{ + +} + +void BattleGroundAB::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + if( GetStatus() != STATUS_IN_PROGRESS ) + return; + + switch(Trigger) + { + case 3948: // Arathi Basin Alliance Exit. + if( Source->GetTeam() != ALLIANCE ) + Source->GetSession()->SendAreaTriggerMessage("Only The Alliance can use that portal"); + else + Source->LeaveBattleground(); + break; + case 3949: // Arathi Basin Horde Exit. + if( Source->GetTeam() != HORDE ) + Source->GetSession()->SendAreaTriggerMessage("Only The Horde can use that portal"); + else + Source->LeaveBattleground(); + break; + case 3866: // Stables + case 3869: // Gold Mine + case 3867: // Farm + case 3868: // Lumber Mill + case 3870: // Black Smith + case 4020: // Unk1 + case 4021: // Unk2 + //break; + default: + //sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + //Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } +} + +/* type: 0-neutral, 1-contested, 3-occupied + teamIndex: 0-ally, 1-horde */ +void BattleGroundAB::_CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay) +{ + // Just put it into the queue + if( delay ) + { + m_BannerTimers[node].timer = 2000; + m_BannerTimers[node].type = type; + m_BannerTimers[node].teamIndex = teamIndex; + return; + } + + uint8 obj = node*8 + type + teamIndex; + + SpawnBGObject(obj, RESPAWN_IMMEDIATELY); + + // handle aura with banner + if( !type ) + return; + obj = node * 8 + ((type == BG_AB_NODE_TYPE_OCCUPIED) ? (5 + teamIndex) : 7); + SpawnBGObject(obj, RESPAWN_IMMEDIATELY); +} + +void BattleGroundAB::_DelBanner(uint8 node, uint8 type, uint8 teamIndex) +{ + uint8 obj = node*8 + type + teamIndex; + SpawnBGObject(obj, RESPAWN_ONE_DAY); + + // handle aura with banner + if( !type ) + return; + obj = node * 8 + ((type == BG_AB_NODE_TYPE_OCCUPIED) ? (5 + teamIndex) : 7); + SpawnBGObject(obj, RESPAWN_ONE_DAY); +} + +const char* BattleGroundAB::_GetNodeName(uint8 node) +{ + switch (node) + { + case BG_AB_NODE_STABLES: + return GetMangosString(LANG_BG_AB_NODE_STABLES); + case BG_AB_NODE_BLACKSMITH: + return GetMangosString(LANG_BG_AB_NODE_BLACKSMITH); + case BG_AB_NODE_FARM: + return GetMangosString(LANG_BG_AB_NODE_FARM); + case BG_AB_NODE_LUMBER_MILL: + return GetMangosString(LANG_BG_AB_NODE_LUMBER_MILL); + case BG_AB_NODE_GOLD_MINE: + return GetMangosString(LANG_BG_AB_NODE_GOLD_MINE); + default: + ASSERT(0); + } + return ""; +} + +void BattleGroundAB::FillInitialWorldStates(WorldPacket& data) +{ + const uint8 plusArray[] = {0, 2, 3, 0, 1}; + + // Node icons + for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) + data << uint32(BG_AB_OP_NODEICONS[node]) << uint32((m_Nodes[node]==0)?1:0); + + // Node occupied states + for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) + for (uint8 i = 1; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + data << uint32(BG_AB_OP_NODESTATES[node] + plusArray[i]) << uint32((m_Nodes[node]==i)?1:0); + + // How many bases each team owns + uint8 ally = 0, horde = 0; + for (uint8 node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) + if( m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_OCCUPIED ) + ++ally; + else if( m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_OCCUPIED ) + ++horde; + + data << uint32(BG_AB_OP_OCCUPIED_BASES_ALLY) << uint32(ally); + data << uint32(BG_AB_OP_OCCUPIED_BASES_HORDE) << uint32(horde); + + // Team scores + data << uint32(BG_AB_OP_RESOURCES_MAX) << uint32(BG_AB_MAX_TEAM_SCORE); + data << uint32(BG_AB_OP_RESOURCES_WARNING) << uint32(BG_AB_WARNING_SCORE); + data << uint32(BG_AB_OP_RESOURCES_ALLY) << uint32(m_TeamScores[BG_TEAM_ALLIANCE]); + data << uint32(BG_AB_OP_RESOURCES_HORDE) << uint32(m_TeamScores[BG_TEAM_HORDE]); + + // other unknown + data << uint32(0x745) << uint32(0x2); // 37 1861 unk +} + +void BattleGroundAB::_SendNodeUpdate(uint8 node) +{ + // Send node owner state update to refresh map icons on client + const uint8 plusArray[] = {0, 2, 3, 0, 1}; + + if( m_prevNodes[node] ) + UpdateWorldState(BG_AB_OP_NODESTATES[node] + plusArray[m_prevNodes[node]], 0); + else + UpdateWorldState(BG_AB_OP_NODEICONS[node], 0); + + UpdateWorldState(BG_AB_OP_NODESTATES[node] + plusArray[m_Nodes[node]], 1); + + // How many bases each team owns + uint8 ally = 0, horde = 0; + for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + if( m_Nodes[i] == BG_AB_NODE_STATUS_ALLY_OCCUPIED ) + ++ally; + else if( m_Nodes[i] == BG_AB_NODE_STATUS_HORDE_OCCUPIED ) + ++horde; + + UpdateWorldState(BG_AB_OP_OCCUPIED_BASES_ALLY, ally); + UpdateWorldState(BG_AB_OP_OCCUPIED_BASES_HORDE, horde); +} + +void BattleGroundAB::_NodeOccupied(uint8 node,Team team) +{ + if( !AddSpiritGuide(node, BG_AB_SpiritGuidePos[node][0], BG_AB_SpiritGuidePos[node][1], BG_AB_SpiritGuidePos[node][2], BG_AB_SpiritGuidePos[node][3], team) ) + sLog.outError("Failed to spawn spirit guide! point: %u, team: %u,", node, team); + + uint8 capturedNodes = 0; + for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + if( m_Nodes[node] == GetTeamIndexByTeamId(team) + BG_AB_NODE_TYPE_OCCUPIED && !m_NodeTimers[i]) + ++capturedNodes; + } + if(capturedNodes >= 5) + CastSpellOnTeam(SPELL_AB_QUEST_REWARD_5_BASES, team); + if(capturedNodes >= 4) + CastSpellOnTeam(SPELL_AB_QUEST_REWARD_4_BASES, team); +} + +void BattleGroundAB::_NodeDeOccupied(uint8 node) +{ + if( node >= BG_AB_DYNAMIC_NODES_COUNT) + return; + + // Those who are waiting to resurrect at this node are taken to the closest own node's graveyard + std::vector ghost_list = m_ReviveQueue[m_BgCreatures[node]]; + if( !ghost_list.empty() ) + { + WorldSafeLocsEntry const *ClosestGrave = NULL; + Player *plr; + for (std::vector::iterator itr = ghost_list.begin(); itr != ghost_list.end(); ++itr) + { + plr = objmgr.GetPlayer(*ghost_list.begin()); + if( !plr ) + continue; + if( !ClosestGrave ) + ClosestGrave = GetClosestGraveYard(plr->GetPositionX(), plr->GetPositionY(), plr->GetPositionZ(), plr->GetTeam()); + + plr->TeleportTo(GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation()); + } + } + + if( m_BgCreatures[node] ) + DelCreature(node); + + // buff object isn't despawned +} + +/* Invoked if a player used a banner as a gameobject */ +void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*target_obj*/) +{ + if( GetStatus() != STATUS_IN_PROGRESS ) + return; + + uint8 node = BG_AB_NODE_STABLES; + GameObject* obj=HashMapHolder::Find(m_BgObjects[node*8+7]); + while ( (node < BG_AB_DYNAMIC_NODES_COUNT) && ((!obj) || (!source->IsWithinDistInMap(obj,10)))) + { + ++node; + obj=HashMapHolder::Find(m_BgObjects[node*8+BG_AB_OBJECT_AURA_CONTESTED]); + } + + if( node == BG_AB_DYNAMIC_NODES_COUNT) + { + // this means our player isn't close to any of banners - maybe cheater ?? + return; + } + + uint8 teamIndex = GetTeamIndexByTeamId(source->GetTeam()); + + // Message to chatlog + char buf[256]; + uint8 type = (teamIndex == 0) ? CHAT_MSG_BG_SYSTEM_ALLIANCE : CHAT_MSG_BG_SYSTEM_HORDE; + + // Check if player really could use this banner, not cheated + if( !(m_Nodes[node] == 0 || teamIndex == m_Nodes[node]%2) ) + return; + + source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); + uint32 sound = 0; + // If node is neutral, change to contested + if( m_Nodes[node] == BG_AB_NODE_TYPE_NEUTRAL ) + { + UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); + m_prevNodes[node] = m_Nodes[node]; + m_Nodes[node] = teamIndex + 1; + // burn current neutral banner + _DelBanner(node, BG_AB_NODE_TYPE_NEUTRAL, 0); + // create new contested banner + _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); + _SendNodeUpdate(node); + m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; + sprintf(buf, GetMangosString(LANG_BG_AB_NODE_CLAIMED), _GetNodeName(node), (teamIndex == 0) ? GetMangosString(LANG_BG_AB_ALLY) : GetMangosString(LANG_BG_AB_HORDE)); + sound = SOUND_NODE_CLAIMED; + } + // If node is contested + else if( (m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_CONTESTED) || (m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_CONTESTED) ) + { + // If last state is NOT occupied, change node to enemy-contested + if( m_prevNodes[node] < BG_AB_NODE_TYPE_OCCUPIED ) + { + UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); + m_prevNodes[node] = m_Nodes[node]; + m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_CONTESTED; + // burn current contested banner + _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, !teamIndex); + // create new contested banner + _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); + _SendNodeUpdate(node); + m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; + sprintf(buf, GetMangosString(LANG_BG_AB_NODE_ASSAULTED), _GetNodeName(node)); + } + // If contested, change back to occupied + else + { + UpdatePlayerScore(source, SCORE_BASES_DEFENDED, 1); + m_prevNodes[node] = m_Nodes[node]; + m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_OCCUPIED; + // burn current contested banner + _DelBanner(node, BG_AB_NODE_TYPE_CONTESTED, !teamIndex); + // create new occupied banner + _CreateBanner(node, BG_AB_NODE_TYPE_OCCUPIED, teamIndex, true); + _SendNodeUpdate(node); + m_NodeTimers[node] = 0; + _NodeOccupied(node,(teamIndex == 0) ? ALLIANCE:HORDE); + sprintf(buf, GetMangosString(LANG_BG_AB_NODE_DEFENDED), _GetNodeName(node)); + } + sound = (teamIndex == 0) ? SOUND_NODE_ASSAULTED_ALLIANCE : SOUND_NODE_ASSAULTED_HORDE; + } + // If node is occupied, change to enemy-contested + else + { + UpdatePlayerScore(source, SCORE_BASES_ASSAULTED, 1); + m_prevNodes[node] = m_Nodes[node]; + m_Nodes[node] = teamIndex + BG_AB_NODE_TYPE_CONTESTED; + // burn current occupied banner + _DelBanner(node, BG_AB_NODE_TYPE_OCCUPIED, !teamIndex); + // create new contested banner + _CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true); + _SendNodeUpdate(node); + _NodeDeOccupied(node); + m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME; + sprintf(buf, GetMangosString(LANG_BG_AB_NODE_ASSAULTED), _GetNodeName(node)); + sound = (teamIndex == 0) ? SOUND_NODE_ASSAULTED_ALLIANCE : SOUND_NODE_ASSAULTED_HORDE; + } + WorldPacket data; + ChatHandler::FillMessageData(&data, source->GetSession(), type, LANG_UNIVERSAL, NULL, source->GetGUID(), buf, NULL); + SendPacketToAll(&data); + // If node is occupied again, send "X has taken the Y" msg. + if( m_Nodes[node] >= BG_AB_NODE_TYPE_OCCUPIED ) + { + sprintf(buf, GetMangosString(LANG_BG_AB_NODE_TAKEN), (teamIndex == 0) ? GetMangosString(LANG_BG_AB_ALLY) : GetMangosString(LANG_BG_AB_HORDE), _GetNodeName(node)); + ChatHandler::FillMessageData(&data, NULL, type, LANG_UNIVERSAL, NULL, 0, buf, NULL); + SendPacketToAll(&data); + } + PlaySoundToAll(sound); +} + +bool BattleGroundAB::SetupBattleGround() +{ + for (int i = 0 ; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + if( !AddObject(BG_AB_OBJECT_BANNER_NEUTRAL + 8*i,BG_AB_OBJECTID_NODE_BANNER_0 + i,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_BANNER_CONT_A + 8*i,BG_AB_OBJECTID_BANNER_CONT_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_BANNER_CONT_H + 8*i,BG_AB_OBJECTID_BANNER_CONT_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_BANNER_ALLY + 8*i,BG_AB_OBJECTID_BANNER_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_BANNER_HORDE + 8*i,BG_AB_OBJECTID_BANNER_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_AURA_ALLY + 8*i,BG_AB_OBJECTID_AURA_A,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_AURA_HORDE + 8*i,BG_AB_OBJECTID_AURA_H,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_AURA_CONTESTED + 8*i,BG_AB_OBJECTID_AURA_C,BG_AB_NodePositions[i][0],BG_AB_NodePositions[i][1],BG_AB_NodePositions[i][2],BG_AB_NodePositions[i][3], 0, 0, sin(BG_AB_NodePositions[i][3]/2), cos(BG_AB_NodePositions[i][3]/2),RESPAWN_ONE_DAY) + ) + { + sLog.outErrorDb("BatteGroundAB: Failed to spawn some object BattleGround not created!"); + return false; + } + } + if( !AddObject(BG_AB_OBJECT_GATE_A,BG_AB_OBJECTID_GATE_A,BG_AB_DoorPositions[0][0],BG_AB_DoorPositions[0][1],BG_AB_DoorPositions[0][2],BG_AB_DoorPositions[0][3],BG_AB_DoorPositions[0][4],BG_AB_DoorPositions[0][5],BG_AB_DoorPositions[0][6],BG_AB_DoorPositions[0][7],RESPAWN_IMMEDIATELY) + || !AddObject(BG_AB_OBJECT_GATE_H,BG_AB_OBJECTID_GATE_H,BG_AB_DoorPositions[1][0],BG_AB_DoorPositions[1][1],BG_AB_DoorPositions[1][2],BG_AB_DoorPositions[1][3],BG_AB_DoorPositions[1][4],BG_AB_DoorPositions[1][5],BG_AB_DoorPositions[1][6],BG_AB_DoorPositions[1][7],RESPAWN_IMMEDIATELY) + ) + { + sLog.outErrorDb("BatteGroundAB: Failed to spawn door object BattleGround not created!"); + return false; + } + //buffs + for (int i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + if( !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i, Buff_Entries[0], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i + 1, Buff_Entries[1], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) + || !AddObject(BG_AB_OBJECT_SPEEDBUFF_STABLES + 3 * i + 2, Buff_Entries[2], BG_AB_BuffPositions[i][0], BG_AB_BuffPositions[i][1], BG_AB_BuffPositions[i][2], BG_AB_BuffPositions[i][3], 0, 0, sin(BG_AB_BuffPositions[i][3]/2), cos(BG_AB_BuffPositions[i][3]/2), RESPAWN_ONE_DAY) + ) + sLog.outErrorDb("BatteGroundAB: Failed to spawn buff object!"); + } + + return true; +} + +void BattleGroundAB::ResetBGSubclass() +{ + m_TeamScores[BG_TEAM_ALLIANCE] = 0; + m_TeamScores[BG_TEAM_HORDE] = 0; + m_lastTick[BG_TEAM_ALLIANCE] = 0; + m_lastTick[BG_TEAM_HORDE] = 0; + m_HonorScoreTics[BG_TEAM_ALLIANCE] = 0; + m_HonorScoreTics[BG_TEAM_HORDE] = 0; + m_ReputationScoreTics[BG_TEAM_ALLIANCE] = 0; + m_ReputationScoreTics[BG_TEAM_HORDE] = 0; + m_IsInformedNearVictory = false; + for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + { + m_Nodes[i] = 0; + m_prevNodes[i] = 0; + m_NodeTimers[i] = 0; + m_BannerTimers[i].timer = 0; + } + + for (uint8 i = 0; i < BG_AB_ALL_NODES_COUNT; ++i) + if(m_BgCreatures[i]) + DelCreature(i); +} + +WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(float x, float y, float /*z*/, uint32 team) +{ + uint8 teamIndex = GetTeamIndexByTeamId(team); + + // Is there any occupied node for this team? + std::vector nodes; + for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i) + if( m_Nodes[i] == teamIndex + 3 ) + nodes.push_back(i); + + WorldSafeLocsEntry const* good_entry = NULL; + // If so, select the closest node to place ghost on + if( !nodes.empty() ) + { + float mindist = 999999.0f; + for (uint8 i = 0; i < nodes.size(); ++i) + { + WorldSafeLocsEntry const*entry = sWorldSafeLocsStore.LookupEntry( BG_AB_GraveyardIds[nodes[i]] ); + if( !entry ) + continue; + float dist = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y); + if( mindist > dist ) + { + mindist = dist; + good_entry = entry; + } + } + nodes.clear(); + } + // If not, place ghost on starting location + if( !good_entry ) + good_entry = sWorldSafeLocsStore.LookupEntry( BG_AB_GraveyardIds[teamIndex+5] ); + + return good_entry; +} + +void BattleGroundAB::UpdatePlayerScore(Player *Source, uint32 type, uint32 value) +{ + std::map::iterator itr = m_PlayerScores.find(Source->GetGUID()); + + if( itr == m_PlayerScores.end() ) // player not found... + return; + + switch(type) + { + case SCORE_BASES_ASSAULTED: + ((BattleGroundABScore*)itr->second)->BasesAssaulted += value; + break; + case SCORE_BASES_DEFENDED: + ((BattleGroundABScore*)itr->second)->BasesDefended += value; + break; + default: + BattleGround::UpdatePlayerScore(Source,type,value); + break; + } +} diff --git a/src/game/BattleGroundAB.h b/src/game/BattleGroundAB.h new file mode 100644 index 00000000000..289d26f98fd --- /dev/null +++ b/src/game/BattleGroundAB.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDAB_H +#define __BATTLEGROUNDAB_H + +class BattleGround; + +enum BG_AB_WorldStates +{ + BG_AB_OP_OCCUPIED_BASES_HORDE = 1778, + BG_AB_OP_OCCUPIED_BASES_ALLY = 1779, + BG_AB_OP_RESOURCES_ALLY = 1776, + BG_AB_OP_RESOURCES_HORDE = 1777, + BG_AB_OP_RESOURCES_MAX = 1780, + BG_AB_OP_RESOURCES_WARNING = 1955 +/* + BG_AB_OP_STABLE_ICON = 1842, //Stable map icon (NONE) + BG_AB_OP_STABLE_STATE_ALIENCE = 1767, //Stable map state (ALIENCE) + BG_AB_OP_STABLE_STATE_HORDE = 1768, //Stable map state (HORDE) + BG_AB_OP_STABLE_STATE_CON_ALI = 1769, //Stable map state (CON ALIENCE) + BG_AB_OP_STABLE_STATE_CON_HOR = 1770, //Stable map state (CON HORDE) + BG_AB_OP_FARM_ICON = 1845, //Farm map icon (NONE) + BG_AB_OP_FARM_STATE_ALIENCE = 1772, //Farm state (ALIENCE) + BG_AB_OP_FARM_STATE_HORDE = 1773, //Farm state (HORDE) + BG_AB_OP_FARM_STATE_CON_ALI = 1774, //Farm state (CON ALIENCE) + BG_AB_OP_FARM_STATE_CON_HOR = 1775, //Farm state (CON HORDE) + + BG_AB_OP_BLACKSMITH_ICON = 1846, //Blacksmith map icon (NONE) + BG_AB_OP_BLACKSMITH_STATE_ALIENCE = 1782, //Blacksmith map state (ALIENCE) + BG_AB_OP_BLACKSMITH_STATE_HORDE = 1783, //Blacksmith map state (HORDE) + BG_AB_OP_BLACKSMITH_STATE_CON_ALI = 1784, //Blacksmith map state (CON ALIENCE) + BG_AB_OP_BLACKSMITH_STATE_CON_HOR = 1785, //Blacksmith map state (CON HORDE) + BG_AB_OP_LUMBERMILL_ICON = 1844, //Lumber Mill map icon (NONE) + BG_AB_OP_LUMBERMILL_STATE_ALIENCE = 1792, //Lumber Mill map state (ALIENCE) + BG_AB_OP_LUMBERMILL_STATE_HORDE = 1793, //Lumber Mill map state (HORDE) + BG_AB_OP_LUMBERMILL_STATE_CON_ALI = 1794, //Lumber Mill map state (CON ALIENCE) + BG_AB_OP_LUMBERMILL_STATE_CON_HOR = 1795, //Lumber Mill map state (CON HORDE) + BG_AB_OP_GOLDMINE_ICON = 1843, //Gold Mine map icon (NONE) + BG_AB_OP_GOLDMINE_STATE_ALIENCE = 1787, //Gold Mine map state (ALIENCE) + BG_AB_OP_GOLDMINE_STATE_HORDE = 1788, //Gold Mine map state (HORDE) + BG_AB_OP_GOLDMINE_STATE_CON_ALI = 1789, //Gold Mine map state (CON ALIENCE + BG_AB_OP_GOLDMINE_STATE_CON_HOR = 1790, //Gold Mine map state (CON HORDE) +*/ +}; + +const uint32 BG_AB_OP_NODESTATES[5] = {1767, 1782, 1772, 1792, 1787}; + +const uint32 BG_AB_OP_NODEICONS[5] = {1842, 1846, 1845, 1844, 1843}; + +/* Note: code uses that these IDs follow each other */ +enum BG_AB_NodeObjectId +{ + BG_AB_OBJECTID_NODE_BANNER_0 = 180087, // Stables banner + BG_AB_OBJECTID_NODE_BANNER_1 = 180088, // Blacksmith banner + BG_AB_OBJECTID_NODE_BANNER_2 = 180089, // Farm banner + BG_AB_OBJECTID_NODE_BANNER_3 = 180090, // Lumber mill banner + BG_AB_OBJECTID_NODE_BANNER_4 = 180091 // Gold mine banner +}; + +enum BG_AB_ObjectType +{ + // for all 5 node points 8*5=40 objects + BG_AB_OBJECT_BANNER_NEUTRAL = 0, + BG_AB_OBJECT_BANNER_CONT_A = 1, + BG_AB_OBJECT_BANNER_CONT_H = 2, + BG_AB_OBJECT_BANNER_ALLY = 3, + BG_AB_OBJECT_BANNER_HORDE = 4, + BG_AB_OBJECT_AURA_ALLY = 5, + BG_AB_OBJECT_AURA_HORDE = 6, + BG_AB_OBJECT_AURA_CONTESTED = 7, + //gates + BG_AB_OBJECT_GATE_A = 40, + BG_AB_OBJECT_GATE_H = 41, + //buffs + BG_AB_OBJECT_SPEEDBUFF_STABLES = 42, + BG_AB_OBJECT_REGENBUFF_STABLES = 43, + BG_AB_OBJECT_BERSERKBUFF_STABLES = 44, + BG_AB_OBJECT_SPEEDBUFF_BLACKSMITH = 45, + BG_AB_OBJECT_REGENBUFF_BLACKSMITH = 46, + BG_AB_OBJECT_BERSERKBUFF_BLACKSMITH = 47, + BG_AB_OBJECT_SPEEDBUFF_FARM = 48, + BG_AB_OBJECT_REGENBUFF_FARM = 49, + BG_AB_OBJECT_BERSERKBUFF_FARM = 50, + BG_AB_OBJECT_SPEEDBUFF_LUMBER_MILL = 51, + BG_AB_OBJECT_REGENBUFF_LUMBER_MILL = 52, + BG_AB_OBJECT_BERSERKBUFF_LUMBER_MILL = 53, + BG_AB_OBJECT_SPEEDBUFF_GOLD_MINE = 54, + BG_AB_OBJECT_REGENBUFF_GOLD_MINE = 55, + BG_AB_OBJECT_BERSERKBUFF_GOLD_MINE = 56, + BG_AB_OBJECT_MAX = 57, +}; + +/* Object id templates from DB */ +enum BG_AB_ObjectTypes +{ + BG_AB_OBJECTID_BANNER_A = 180058, + BG_AB_OBJECTID_BANNER_CONT_A = 180059, + BG_AB_OBJECTID_BANNER_H = 180060, + BG_AB_OBJECTID_BANNER_CONT_H = 180061, + + BG_AB_OBJECTID_AURA_A = 180100, + BG_AB_OBJECTID_AURA_H = 180101, + BG_AB_OBJECTID_AURA_C = 180102, + + BG_AB_OBJECTID_GATE_A = 180255, + BG_AB_OBJECTID_GATE_H = 180256 +}; + +enum BG_AB_Timers +{ + BG_AB_FLAG_CAPTURING_TIME = 60000, +}; + +enum BG_AB_Score +{ + BG_AB_MAX_TEAM_SCORE = 2000, + BG_AB_WARNING_SCORE = 1800 +}; + +/* do NOT change the order, else wrong behaviour */ +enum BG_AB_BattleGroundNodes +{ + BG_AB_NODE_STABLES = 0, + BG_AB_NODE_BLACKSMITH = 1, + BG_AB_NODE_FARM = 2, + BG_AB_NODE_LUMBER_MILL = 3, + BG_AB_NODE_GOLD_MINE = 4, + + BG_AB_DYNAMIC_NODES_COUNT = 5, // dynamic nodes that can be captured + + BG_AB_SPIRIT_ALIANCE = 5, + BG_AB_SPIRIT_HORDE = 6, + + BG_AB_ALL_NODES_COUNT = 7, // all nodes (dynamic and static) +}; + +enum BG_AB_NodeStatus +{ + BG_AB_NODE_TYPE_NEUTRAL = 0, + BG_AB_NODE_TYPE_CONTESTED = 1, + BG_AB_NODE_STATUS_ALLY_CONTESTED = 1, + BG_AB_NODE_STATUS_HORDE_CONTESTED = 2, + BG_AB_NODE_TYPE_OCCUPIED = 3, + BG_AB_NODE_STATUS_ALLY_OCCUPIED = 3, + BG_AB_NODE_STATUS_HORDE_OCCUPIED = 4 +}; + +enum BG_AB_Sounds +{ + SOUND_NODE_CLAIMED = 8192, + SOUND_NODE_CAPTURED_ALLIANCE = 8173, + SOUND_NODE_CAPTURED_HORDE = 8213, + SOUND_NODE_ASSAULTED_ALLIANCE = 8174, + SOUND_NODE_ASSAULTED_HORDE = 8212, + SOUND_NEAR_VICTORY = 8456 +}; + +// x, y, z, o +const float BG_AB_NodePositions[BG_AB_DYNAMIC_NODES_COUNT][4] = { + {1166.785f, 1200.132f, -56.70859f, 0.9075713f}, // stables + {977.0156f, 1046.616f, -44.80923f, -2.600541f}, // blacksmith + {806.1821f, 874.2723f, -55.99371f, -2.303835f}, // farm + {856.1419f, 1148.902f, 11.18469f, -2.303835f}, // lumber mill + {1146.923f, 848.1782f, -110.917f, -0.7330382f} // gold mine +}; + +// x, y, z, o, rot0, rot1, rot2, rot3 +const float BG_AB_DoorPositions[2][8] = { + {1284.597f, 1281.167f, -15.97792f, 0.7068594f, 0.012957f, -0.060288f, 0.344959f, 0.93659f}, + {708.0903f, 708.4479f, -17.8342f, -2.391099f, 0.050291f, 0.015127f, 0.929217f, -0.365784f} +}; + +// Tick intervals and given points: case 0,1,2,3,4,5 captured nodes +const uint32 BG_AB_TickIntervals[6] = {0, 12000, 9000, 6000, 3000, 1000}; +const uint32 BG_AB_TickPoints[6] = {0, 10, 10, 10, 10, 30}; + +// WorldSafeLocs ids for 5 nodes, and for ally, and horde starting location +const uint32 BG_AB_GraveyardIds[BG_AB_ALL_NODES_COUNT] = {895, 894, 893, 897, 896, 898, 899}; + +// x, y, z, o +const float BG_AB_BuffPositions[BG_AB_DYNAMIC_NODES_COUNT][4] = { + {1185.71f, 1185.24f, -56.36f, 2.56f}, // stables + {990.75f, 1008.18f, -42.60f, 2.43f}, // blacksmith + {817.66f, 843.34f, -56.54f, 3.01f}, // farm + {807.46f, 1189.16f, 11.92f, 5.44f}, // lumber mill + {1146.62f, 816.94f, -98.49f, 6.14f} // gold mine +}; + +// x, y, z, o +const float BG_AB_SpiritGuidePos[BG_AB_ALL_NODES_COUNT][4] = { + {1200.03f, 1171.09f, -56.47f, 5.15f}, // stables + {1017.43f, 960.61f, -42.95f, 4.88f}, // blacksmith + {833.00f, 793.00f, -57.25f, 5.27f}, // farm + {775.17f, 1206.40f, 15.79f, 1.90f}, // lumber mill + {1207.48f, 787.00f, -83.36f, 5.51f}, // gold mine + {1354.05f, 1275.48f, -11.30f, 4.77f}, // alliance starting base + {714.61f, 646.15f, -10.87f, 4.34f} // horde starting base +}; + +struct BG_AB_BannerTimer +{ + uint32 timer; + uint8 type; + uint8 teamIndex; +}; + +class BattleGroundABScore : public BattleGroundScore +{ + public: + BattleGroundABScore(): BasesAssaulted(0), BasesDefended(0) {}; + virtual ~BattleGroundABScore() {}; + uint32 BasesAssaulted; + uint32 BasesDefended; +}; + +class BattleGroundAB : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundAB(); + ~BattleGroundAB(); + + void Update(time_t diff); + void AddPlayer(Player *plr); + void RemovePlayer(Player *plr,uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + virtual bool SetupBattleGround(); + virtual void ResetBGSubclass(); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(float x, float y, float z, uint32 team); + + /* Scorekeeping */ + virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); + + virtual void FillInitialWorldStates(WorldPacket& data); + + /* Nodes occupying */ + virtual void EventPlayerClickedOnFlag(Player *source, GameObject* target_obj); + + private: + /* Gameobject spawning/despawning */ + void _CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool delay); + void _DelBanner(uint8 node, uint8 type, uint8 teamIndex); + void _SendNodeUpdate(uint8 node); + + /* Creature spawning/despawning */ + // TODO: working, scripted peons spawning + void _NodeOccupied(uint8 node,Team team); + void _NodeDeOccupied(uint8 node); + + const char* _GetNodeName(uint8 node); + + /* Nodes info: + 0: neutral + 1: ally contested + 2: horde contested + 3: ally occupied + 4: horde occupied */ + uint8 m_Nodes[BG_AB_DYNAMIC_NODES_COUNT]; + uint8 m_prevNodes[BG_AB_DYNAMIC_NODES_COUNT]; + BG_AB_BannerTimer m_BannerTimers[BG_AB_DYNAMIC_NODES_COUNT]; + int32 m_NodeTimers[BG_AB_DYNAMIC_NODES_COUNT]; + uint32 m_TeamScores[2]; + uint32 m_lastTick[2]; + uint32 m_HonorScoreTics[2]; + uint32 m_ReputationScoreTics[2]; + bool m_IsInformedNearVictory; +}; +#endif diff --git a/src/game/BattleGroundAV.cpp b/src/game/BattleGroundAV.cpp new file mode 100644 index 00000000000..78b585be190 --- /dev/null +++ b/src/game/BattleGroundAV.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Object.h" +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundAV.h" +#include "Creature.h" +#include "MapManager.h" +#include "Language.h" + +BattleGroundAV::BattleGroundAV() +{ + +} + +BattleGroundAV::~BattleGroundAV() +{ + +} + +void BattleGroundAV::Update(time_t diff) +{ + BattleGround::Update(diff); +} + +void BattleGroundAV::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundAVScore* sc = new BattleGroundAVScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundAV::RemovePlayer(Player* /*plr*/,uint64 /*guid*/) +{ + +} + +void BattleGroundAV::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + uint32 SpellId = 0; + switch(Trigger) + { + case 95: + case 2606: + case 2608: + case 3326: + case 3327: + case 3328: + case 3329: + case 3330: + case 3331: + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } + + if(SpellId) + Source->CastSpell(Source, SpellId, true); +} + +void BattleGroundAV::UpdatePlayerScore(Player* Source, uint32 type, uint32 value) +{ + + std::map::iterator itr = m_PlayerScores.find(Source->GetGUID()); + + if(itr == m_PlayerScores.end()) // player not found... + return; + + switch(type) + { + case SCORE_GRAVEYARDS_ASSAULTED: + ((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted += value; + break; + case SCORE_GRAVEYARDS_DEFENDED: + ((BattleGroundAVScore*)itr->second)->GraveyardsDefended += value; + break; + case SCORE_TOWERS_ASSAULTED: + ((BattleGroundAVScore*)itr->second)->TowersAssaulted += value; + break; + case SCORE_TOWERS_DEFENDED: + ((BattleGroundAVScore*)itr->second)->TowersDefended += value; + break; + case SCORE_MINES_CAPTURED: + ((BattleGroundAVScore*)itr->second)->MinesCaptured += value; + break; + case SCORE_LEADERS_KILLED: + ((BattleGroundAVScore*)itr->second)->LeadersKilled += value; + break; + case SCORE_SECONDARY_OBJECTIVES: + ((BattleGroundAVScore*)itr->second)->SecondaryObjectives += value; + break; + default: + BattleGround::UpdatePlayerScore(Source,type,value); + break; + } +} diff --git a/src/game/BattleGroundAV.h b/src/game/BattleGroundAV.h new file mode 100644 index 00000000000..441c310fda9 --- /dev/null +++ b/src/game/BattleGroundAV.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDAV_H +#define __BATTLEGROUNDAV_H + +class BattleGround; + +class BattleGroundAVScore : public BattleGroundScore +{ + public: + BattleGroundAVScore() : GraveyardsAssaulted(0), GraveyardsDefended(0), TowersAssaulted(0), TowersDefended(0), MinesCaptured(0), LeadersKilled(0), SecondaryObjectives(0) {}; + virtual ~BattleGroundAVScore() {}; + uint32 GraveyardsAssaulted; + uint32 GraveyardsDefended; + uint32 TowersAssaulted; + uint32 TowersDefended; + uint32 MinesCaptured; + uint32 LeadersKilled; + uint32 SecondaryObjectives; +}; + +class BattleGroundAV : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundAV(); + ~BattleGroundAV(); + void Update(time_t diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + + void RemovePlayer(Player *plr,uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + //bool SetupBattleGround(); + + /* Scorekeeping */ + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); + + private: +}; +#endif diff --git a/src/game/BattleGroundBE.cpp b/src/game/BattleGroundBE.cpp new file mode 100644 index 00000000000..a85286ce6d5 --- /dev/null +++ b/src/game/BattleGroundBE.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Object.h" +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundBE.h" +#include "Creature.h" +#include "ObjectMgr.h" +#include "MapManager.h" +#include "Language.h" + +BattleGroundBE::BattleGroundBE() +{ + m_BgObjects.resize(BG_BE_OBJECT_MAX); +} + +BattleGroundBE::~BattleGroundBE() +{ + +} + +void BattleGroundBE::Update(time_t diff) +{ + BattleGround::Update(diff); + + // after bg start we get there + if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) + { + ModifyStartDelayTime(diff); + + if (!(m_Events & 0x01)) + { + m_Events |= 0x01; + for(uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_4; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + + for(uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; i++) + SpawnBGObject(i, RESPAWN_ONE_DAY); + + SetStartDelayTime(START_DELAY1); + SendMessageToAll(LANG_ARENA_ONE_MINUTE); + } + // After 30 seconds, warning is signalled + else if (GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x04)) + { + m_Events |= 0x04; + SendMessageToAll(LANG_ARENA_THIRTY_SECONDS); + } + // After 15 seconds, warning is signalled + else if (GetStartDelayTime() <= START_DELAY3 && !(m_Events & 0x08)) + { + m_Events |= 0x08; + SendMessageToAll(LANG_ARENA_FIFTEEN_SECONDS); + } + // delay expired (1 minute) + else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10)) + { + m_Events |= 0x10; + + for(uint32 i = BG_BE_OBJECT_DOOR_1; i <= BG_BE_OBJECT_DOOR_2; i++) + DoorOpen(i); + + for(uint32 i = BG_BE_OBJECT_BUFF_1; i <= BG_BE_OBJECT_BUFF_2; i++) + SpawnBGObject(i, 60); + + SendMessageToAll(LANG_ARENA_BEGUN); + SetStatus(STATUS_IN_PROGRESS); + SetStartDelayTime(0); + + for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if(Player *plr = objmgr.GetPlayer(itr->first)) + plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + } + } + + /*if(GetStatus() == STATUS_IN_PROGRESS) + { + // update something + }*/ +} + +void BattleGroundBE::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundBEScore* sc = new BattleGroundBEScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundBE::RemovePlayer(Player *plr, uint64 guid) +{ + +} + +void BattleGroundBE::HandleKillPlayer(Player *player, Player *killer) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + if(!killer) + { + sLog.outError("Killer player not found"); + return; + } + + BattleGround::HandleKillPlayer(player, killer); + + uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + + ++m_TeamKills[killer_team_index]; // add kills to killer's team + + if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + { + // all opponents killed + EndBattleGround(killer->GetTeam()); + } +} + +void BattleGroundBE::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + //uint32 SpellId = 0; + //uint64 buff_guid = 0; + switch(Trigger) + { + case 4538: // buff trigger? + //buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_1]; + break; + case 4539: // buff trigger? + //buff_guid = m_BgObjects[BG_BE_OBJECT_BUFF_2]; + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } + + //if(buff_guid) + // HandleTriggerBuff(buff_guid,Source); +} + +void BattleGroundBE::ResetBGSubclass() +{ + m_TeamKills[BG_TEAM_ALLIANCE] = 0; + m_TeamKills[BG_TEAM_HORDE] = 0; +} + +bool BattleGroundBE::SetupBattleGround() +{ + // gates + if( !AddObject(BG_BE_OBJECT_DOOR_1, BG_BE_OBJECT_TYPE_DOOR_1, 6287.277f, 282.1877f, 3.810925f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_BE_OBJECT_DOOR_2, BG_BE_OBJECT_TYPE_DOOR_2, 6189.546f, 241.7099f, 3.101481f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_BE_OBJECT_DOOR_3, BG_BE_OBJECT_TYPE_DOOR_3, 6299.116f, 296.5494f, 3.308032f, 0.8813917f, 0, 0, 0.4265689f, 0.9044551f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_BE_OBJECT_DOOR_4, BG_BE_OBJECT_TYPE_DOOR_4, 6177.708f, 227.3481f, 3.604374f, -2.260201f, 0, 0, 0.9044551f, -0.4265689f, RESPAWN_IMMEDIATELY) + // buffs + || !AddObject(BG_BE_OBJECT_BUFF_1, BG_BE_OBJECT_TYPE_BUFF_1, 6249.042f, 275.3239f, 11.22033f, -1.448624f, 0, 0, 0.6626201f, -0.7489557f, 120) + || !AddObject(BG_BE_OBJECT_BUFF_2, BG_BE_OBJECT_TYPE_BUFF_2, 6228.26f, 249.566f, 11.21812f, -0.06981307f, 0, 0, 0.03489945f, -0.9993908f, 120)) + { + sLog.outErrorDb("BatteGroundBE: Failed to spawn some object!"); + return false; + } + + return true; +} + +void BattleGroundBE::UpdatePlayerScore(Player* Source, uint32 type, uint32 value) +{ + + std::map::iterator itr = m_PlayerScores.find(Source->GetGUID()); + + if(itr == m_PlayerScores.end()) // player not found... + return; + + //there is nothing special in this score + BattleGround::UpdatePlayerScore(Source,type,value); + +} + +/* +21:45:46 id:231310 [S2C] SMSG_INIT_WORLD_STATES (706 = 0x02C2) len: 86 +0000: 32 02 00 00 76 0e 00 00 00 00 00 00 09 00 f3 09 | 2...v........... +0010: 00 00 01 00 00 00 f1 09 00 00 01 00 00 00 f0 09 | ................ +0020: 00 00 02 00 00 00 d4 08 00 00 00 00 00 00 d8 08 | ................ +0030: 00 00 00 00 00 00 d7 08 00 00 00 00 00 00 d6 08 | ................ +0040: 00 00 00 00 00 00 d5 08 00 00 00 00 00 00 d3 08 | ................ +0050: 00 00 00 00 00 00 | ...... + +spell 32724 - Gold Team +spell 32725 - Green Team +35774 Gold Team +35775 Green Team +*/ diff --git a/src/game/BattleGroundBE.h b/src/game/BattleGroundBE.h new file mode 100644 index 00000000000..1b92faab9fc --- /dev/null +++ b/src/game/BattleGroundBE.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDBE_H +#define __BATTLEGROUNDBE_H + +class BattleGround; + +enum BattleGroundBEObjectTypes +{ + BG_BE_OBJECT_DOOR_1 = 0, + BG_BE_OBJECT_DOOR_2 = 1, + BG_BE_OBJECT_DOOR_3 = 2, + BG_BE_OBJECT_DOOR_4 = 3, + BG_BE_OBJECT_BUFF_1 = 4, + BG_BE_OBJECT_BUFF_2 = 5, + BG_BE_OBJECT_MAX = 6 +}; + +enum BattleGroundBEObjects +{ + BG_BE_OBJECT_TYPE_DOOR_1 = 183971, + BG_BE_OBJECT_TYPE_DOOR_2 = 183973, + BG_BE_OBJECT_TYPE_DOOR_3 = 183970, + BG_BE_OBJECT_TYPE_DOOR_4 = 183972, + BG_BE_OBJECT_TYPE_BUFF_1 = 184663, + BG_BE_OBJECT_TYPE_BUFF_2 = 184664 +}; + +class BattleGroundBEScore : public BattleGroundScore +{ + public: + BattleGroundBEScore() {}; + virtual ~BattleGroundBEScore() {}; +}; + +class BattleGroundBE : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundBE(); + ~BattleGroundBE(); + void Update(time_t diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + void ResetBGSubclass(); + void HandleKillPlayer(Player* player, Player *killer); + + /* Scorekeeping */ + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); + + private: + uint32 m_TeamKills[2]; // count of kills for each team +}; +#endif diff --git a/src/game/BattleGroundEY.cpp b/src/game/BattleGroundEY.cpp new file mode 100644 index 00000000000..dbde3295f4d --- /dev/null +++ b/src/game/BattleGroundEY.cpp @@ -0,0 +1,909 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Object.h" +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundEY.h" +#include "Creature.h" +#include "Chat.h" +#include "ObjectMgr.h" +#include "MapManager.h" +#include "Language.h" +#include "Util.h" + +BattleGroundEY::BattleGroundEY() +{ + m_BuffChange = true; + m_BgObjects.resize(BG_EY_OBJECT_MAX); + m_BgCreatures.resize(BG_EY_CREATURES_MAX); + m_Points_Trigger[FEL_REALVER] = TR_FEL_REALVER_BUFF; + m_Points_Trigger[BLOOD_ELF] = TR_BLOOD_ELF_BUFF; + m_Points_Trigger[DRAENEI_RUINS] = TR_DRAENEI_RUINS_BUFF; + m_Points_Trigger[MAGE_TOWER] = TR_MAGE_TOWER_BUFF; +} + +BattleGroundEY::~BattleGroundEY() +{ +} + +void BattleGroundEY::Update(time_t diff) +{ + BattleGround::Update(diff); + // after bg start we get there (once) + if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) + { + ModifyStartDelayTime(diff); + + if(!(m_Events & 0x01)) + { + m_Events |= 0x01; + + SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_IMMEDIATELY); + + for(uint32 i = BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER; i < BG_EY_OBJECT_MAX; ++i) + SpawnBGObject(i, RESPAWN_ONE_DAY); + + SetStartDelayTime(START_DELAY0); + } + // After 1 minute, warning is signalled + else if(GetStartDelayTime() <= START_DELAY1 && !(m_Events & 0x04)) + { + m_Events |= 0x04; + SendMessageToAll(GetMangosString(LANG_BG_EY_ONE_MINUTE)); + } + // After 1,5 minute, warning is signalled + else if(GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x08)) + { + m_Events |= 0x08; + SendMessageToAll(GetMangosString(LANG_BG_EY_HALF_MINUTE)); + } + // After 2 minutes, gates OPEN ! x) + else if(GetStartDelayTime() < 0 && !(m_Events & 0x10)) + { + m_Events |= 0x10; + SpawnBGObject(BG_EY_OBJECT_DOOR_A, RESPAWN_ONE_DAY); + SpawnBGObject(BG_EY_OBJECT_DOOR_H, RESPAWN_ONE_DAY); + + for(uint32 i = BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER; i <= BG_EY_OBJECT_FLAG_NETHERSTORM; ++i) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + for(uint32 i = 0; i < EY_POINTS_MAX; ++i) + { + //randomly spawn buff + uint8 buff = urand(0, 2); + SpawnBGObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + buff + i * 3, RESPAWN_IMMEDIATELY); + } + + SendMessageToAll(GetMangosString(LANG_BG_EY_BEGIN)); + + PlaySoundToAll(SOUND_BG_START); + SetStatus(STATUS_IN_PROGRESS); + + for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if(Player *plr = objmgr.GetPlayer(itr->first)) + plr->RemoveAurasDueToSpell(SPELL_PREPARATION); + } + } + else if(GetStatus() == STATUS_IN_PROGRESS) + { + m_PointAddingTimer -= diff; + if(m_PointAddingTimer <= 0) + { + m_PointAddingTimer = BG_EY_FPOINTS_TICK_TIME; + if (m_TeamPointsCount[BG_TEAM_ALLIANCE] > 0) + AddPoints(ALLIANCE, BG_EY_TickPoints[m_TeamPointsCount[BG_TEAM_ALLIANCE] - 1]); + if (m_TeamPointsCount[BG_TEAM_HORDE] > 0) + AddPoints(HORDE, BG_EY_TickPoints[m_TeamPointsCount[BG_TEAM_HORDE] - 1]); + } + + if(m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN || m_FlagState == BG_EY_FLAG_STATE_ON_GROUND) + { + m_FlagsTimer -= diff; + + if(m_FlagsTimer < 0) + { + m_FlagsTimer = 0; + if (m_FlagState == BG_EY_FLAG_STATE_WAIT_RESPAWN) + RespawnFlag(true); + else + RespawnFlagAfterDrop(); + } + } + + m_TowerCapCheckTimer -= diff; + if(m_TowerCapCheckTimer <= 0) + { + //check if player joined point + /*I used this order of calls, because although we will check if one player is in gameobject's distance 2 times + but we can count of players on current point in CheckSomeoneLeftPoint + */ + this->CheckSomeoneJoinedPoint(); + //check if player left point + this->CheckSomeoneLeftPoint(); + this->UpdatePointStatuses(); + m_TowerCapCheckTimer = BG_EY_FPOINTS_TICK_TIME; + } + } +} + +void BattleGroundEY::AddPoints(uint32 Team, uint32 Points) +{ + uint8 team_index = GetTeamIndexByTeamId(Team); + m_TeamScores[team_index] += Points; + m_HonorScoreTics[team_index] += Points; + if (m_HonorScoreTics[team_index] >= BG_HONOR_SCORE_TICKS) + { + RewardHonorToTeam(20, Team); + m_HonorScoreTics[team_index] -= BG_HONOR_SCORE_TICKS; + } + UpdateTeamScore(Team); +} + +void BattleGroundEY::CheckSomeoneJoinedPoint() +{ + GameObject *obj = NULL; + for (uint8 i = 0; i < EY_POINTS_MAX; ++i) + { + obj = HashMapHolder::Find(m_BgObjects[BG_EY_OBJECT_TOWER_CAP_FEL_REALVER + i]); + if (obj) + { + uint8 j = 0; + while (j < m_PlayersNearPoint[EY_POINTS_MAX].size()) + { + Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[EY_POINTS_MAX][j]); + if(!plr) + { + sLog.outError("BattleGroundEY: Player " I64FMTD " not found!", m_PlayersNearPoint[EY_POINTS_MAX][j]); + ++j; + continue; + } + if (plr->isAlive() && plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS)) + { + //player joined point! + //show progress bar + UpdateWorldStateForPlayer(PROGRESS_BAR_PERCENT_GREY, BG_EY_PROGRESS_BAR_PERCENT_GREY, plr); + UpdateWorldStateForPlayer(PROGRESS_BAR_STATUS, m_PointBarStatus[i], plr); + UpdateWorldStateForPlayer(PROGRESS_BAR_SHOW, BG_EY_PROGRESS_BAR_SHOW, plr); + //add player to point + m_PlayersNearPoint[i].push_back(m_PlayersNearPoint[EY_POINTS_MAX][j]); + //remove player from "free space" + m_PlayersNearPoint[EY_POINTS_MAX].erase(m_PlayersNearPoint[EY_POINTS_MAX].begin() + j); + } + else + ++j; + } + } + } +} + +void BattleGroundEY::CheckSomeoneLeftPoint() +{ + //reset current point counts + for (uint8 i = 0; i < 2*EY_POINTS_MAX; ++i) + m_CurrentPointPlayersCount[i] = 0; + GameObject *obj = NULL; + for(uint8 i = 0; i < EY_POINTS_MAX; ++i) + { + obj = HashMapHolder::Find(m_BgObjects[BG_EY_OBJECT_TOWER_CAP_FEL_REALVER + i]); + if(obj) + { + uint8 j = 0; + while (j < m_PlayersNearPoint[i].size()) + { + Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[i][j]); + if (!plr) + { + sLog.outError("BattleGroundEY: Player " I64FMTD " not found!", m_PlayersNearPoint[i][j]); + //move not existed player to "free space" - this will cause many error showing in log, but it is a very important bug + m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]); + m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j); + ++j; + continue; + } + if (!plr->isAlive() || !plr->IsWithinDistInMap(obj, BG_EY_POINT_RADIUS)) + //move player out of point (add him to players that are out of points + { + m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]); + m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j); + this->UpdateWorldStateForPlayer(PROGRESS_BAR_SHOW, BG_EY_PROGRESS_BAR_DONT_SHOW, plr); + } + else + { + //player is neat flag, so update count: + m_CurrentPointPlayersCount[2 * i + GetTeamIndexByTeamId(plr->GetTeam())]++; + ++j; + } + } + } + } +} + +void BattleGroundEY::UpdatePointStatuses() +{ + for(uint8 point = 0; point < EY_POINTS_MAX; ++point) + { + if (m_PlayersNearPoint[point].empty()) + continue; + //count new point bar status: + m_PointBarStatus[point] += (m_CurrentPointPlayersCount[2 * point] - m_CurrentPointPlayersCount[2 * point + 1] < BG_EY_POINT_MAX_CAPTURERS_COUNT) ? m_CurrentPointPlayersCount[2 * point] - m_CurrentPointPlayersCount[2 * point + 1] : BG_EY_POINT_MAX_CAPTURERS_COUNT; + + if (m_PointBarStatus[point] > BG_EY_PROGRESS_BAR_ALI_CONTROLLED) + //point is fully alliance's + m_PointBarStatus[point] = BG_EY_PROGRESS_BAR_ALI_CONTROLLED; + if (m_PointBarStatus[point] < BG_EY_PROGRESS_BAR_HORDE_CONTROLLED) + //point is fully horde's + m_PointBarStatus[point] = BG_EY_PROGRESS_BAR_HORDE_CONTROLLED; + + uint32 pointOwnerTeamId = 0; + //find which team should own this point + if (m_PointBarStatus[point] <= BG_EY_PROGRESS_BAR_NEUTRAL_LOW) + pointOwnerTeamId = HORDE; + else if (m_PointBarStatus[point] >= BG_EY_PROGRESS_BAR_NEUTRAL_HIGH) + pointOwnerTeamId = ALLIANCE; + else + pointOwnerTeamId = EY_POINT_NO_OWNER; + + for (uint8 i = 0; i < m_PlayersNearPoint[point].size(); ++i) + { + Player *plr = objmgr.GetPlayer(m_PlayersNearPoint[point][i]); + if (plr) + { + this->UpdateWorldStateForPlayer(PROGRESS_BAR_STATUS, m_PointBarStatus[point], plr); + //if point owner changed we must evoke event! + if (pointOwnerTeamId != m_PointOwnedByTeam[point]) + { + //point was uncontrolled and player is from team which captured point + if (m_PointState[point] == EY_POINT_STATE_UNCONTROLLED && plr->GetTeam() == pointOwnerTeamId) + this->EventTeamCapturedPoint(plr, point); + + //point was under control and player isn't from team which controlled it + if (m_PointState[point] == EY_POINT_UNDER_CONTROL && plr->GetTeam() != m_PointOwnedByTeam[point]) + this->EventTeamLostPoint(plr, point); + } + } + } + } +} + +void BattleGroundEY::UpdateTeamScore(uint32 Team) +{ + uint32 score = GetTeamScore(Team); + if(score >= EY_MAX_TEAM_SCORE) + { + score = EY_MAX_TEAM_SCORE; + EndBattleGround(Team); + } + + if(Team == ALLIANCE) + UpdateWorldState(EY_ALLIANCE_RESOURCES, score); + else + UpdateWorldState(EY_HORDE_RESOURCES, score); +} + +void BattleGroundEY::UpdatePointsCount(uint32 Team) +{ + if(Team == ALLIANCE) + UpdateWorldState(EY_ALLIANCE_BASE, m_TeamPointsCount[BG_TEAM_ALLIANCE]); + else + UpdateWorldState(EY_HORDE_BASE, m_TeamPointsCount[BG_TEAM_HORDE]); +} + +void BattleGroundEY::UpdatePointsIcons(uint32 Team, uint32 Point) +{ + //we MUST firstly send 0, after that we can send 1!!! + if (m_PointState[Point] == EY_POINT_UNDER_CONTROL) + { + UpdateWorldState(m_PointsIconStruct[Point].WorldStateControlIndex, 0); + if(Team == ALLIANCE) + UpdateWorldState(m_PointsIconStruct[Point].WorldStateAllianceControlledIndex, 1); + else + UpdateWorldState(m_PointsIconStruct[Point].WorldStateHordeControlledIndex, 1); + } + else + { + if(Team == ALLIANCE) + UpdateWorldState(m_PointsIconStruct[Point].WorldStateAllianceControlledIndex, 0); + else + UpdateWorldState(m_PointsIconStruct[Point].WorldStateHordeControlledIndex, 0); + UpdateWorldState(m_PointsIconStruct[Point].WorldStateControlIndex, 1); + } +} + +void BattleGroundEY::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map + BattleGroundEYScore* sc = new BattleGroundEYScore; + + m_PlayersNearPoint[EY_POINTS_MAX].push_back(plr->GetGUID()); + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundEY::RemovePlayer(Player *plr, uint64 guid) +{ + // sometimes flag aura not removed :( + for (int j = EY_POINTS_MAX; j >= 0; --j) + { + for(int i = 0; i < m_PlayersNearPoint[j].size(); ++i) + if(m_PlayersNearPoint[j][i] == guid) + m_PlayersNearPoint[j].erase(m_PlayersNearPoint[j].begin() + i); + } + if(IsFlagPickedup()) + { + if(m_FlagKeeper == guid) + { + if(plr) + this->EventPlayerDroppedFlag(plr); + else + { + SetFlagPicker(0); + RespawnFlag(true); + } + } + } +} + +void BattleGroundEY::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + if(!Source->isAlive()) //hack code, must be removed later + return; + + switch(Trigger) + { + case TR_BLOOD_ELF_POINT: + if(m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[BLOOD_ELF] == Source->GetTeam()) + if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_BLOOD_ELF); + break; + case TR_FEL_REALVER_POINT: + if(m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[FEL_REALVER] == Source->GetTeam()) + if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_FEL_REALVER); + break; + case TR_MAGE_TOWER_POINT: + if(m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[MAGE_TOWER] == Source->GetTeam()) + if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_MAGE_TOWER); + break; + case TR_DRAENEI_RUINS_POINT: + if(m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[DRAENEI_RUINS] == Source->GetTeam()) + if(m_FlagState && GetFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source, BG_EY_OBJECT_FLAG_DRAENEI_RUINS); + break; + case 4512: + case 4515: + case 4517: + case 4519: + case 4530: + case 4531: + case 4568: + case 4569: + case 4570: + case 4571: + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } +} + +bool BattleGroundEY::SetupBattleGround() +{ + // doors + if( !AddObject(BG_EY_OBJECT_DOOR_A, BG_OBJECT_A_DOOR_EY_ENTRY, 2527.6f, 1596.91f, 1262.13f, -3.12414f, -0.173642f, -0.001515f, 0.98477f, -0.008594f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_EY_OBJECT_DOOR_H, BG_OBJECT_H_DOOR_EY_ENTRY, 1803.21f, 1539.49f, 1261.09f, 3.14159f, 0.173648f, 0, 0.984808f, 0, RESPAWN_IMMEDIATELY) + // banners (alliance) + || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_A_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_A_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_A_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_A_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY) + // banners (horde) + || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_H_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_H_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_H_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_H_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY) + // banners (natural) + || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2057.46f, 1735.07f, 1187.91f, -0.925024f, 0, 0, 0.446198f, -0.894934f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2032.25f, 1729.53f, 1190.33f, 1.8675f, 0, 0, 0.803857f, 0.594823f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_FEL_REALVER_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2092.35f, 1775.46f, 1187.08f, -0.401426f, 0, 0, 0.199368f, -0.979925f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2047.19f, 1349.19f, 1189.0f, -1.62316f, 0, 0, 0.725374f, -0.688354f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2074.32f, 1385.78f, 1194.72f, 0.488692f, 0, 0, 0.241922f, 0.970296f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2025.13f, 1386.12f, 1192.74f, 2.3911f, 0, 0, 0.930418f, 0.366501f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2276.8f, 1400.41f, 1196.33f, 2.44346f, 0, 0, 0.939693f, 0.34202f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2305.78f, 1404.56f, 1199.38f, 1.74533f, 0, 0, 0.766044f, 0.642788f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2245.4f, 1366.41f, 1195.28f, 2.21657f, 0, 0, 0.894934f, 0.446198f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_OBJECT_N_BANNER_EY_ENTRY, 2270.84f, 1784.08f, 1186.76f, 2.42601f, 0, 0, 0.936672f, 0.350207f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_LEFT, BG_OBJECT_N_BANNER_EY_ENTRY, 2269.13f, 1737.7f, 1186.66f, 0.994838f, 0, 0, 0.477159f, 0.878817f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_RIGHT, BG_OBJECT_N_BANNER_EY_ENTRY, 2300.86f, 1741.25f, 1187.7f, -0.785398f, 0, 0, 0.382683f, -0.92388f, RESPAWN_ONE_DAY) + // flags + || !AddObject(BG_EY_OBJECT_FLAG_NETHERSTORM, BG_OBJECT_FLAG2_EY_ENTRY, 2174.782227f, 1569.054688f, 1160.361938f, -1.448624f, 0, 0, 0.662620f, -0.748956f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_FLAG_FEL_REALVER, BG_OBJECT_FLAG1_EY_ENTRY, 2044.28f, 1729.68f, 1189.96f, -0.017453f, 0, 0, 0.008727f, -0.999962f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_FLAG_BLOOD_ELF, BG_OBJECT_FLAG1_EY_ENTRY, 2048.83f, 1393.65f, 1194.49f, 0.20944f, 0, 0, 0.104528f, 0.994522f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_FLAG_DRAENEI_RUINS, BG_OBJECT_FLAG1_EY_ENTRY, 2286.56f, 1402.36f, 1197.11f, 3.72381f, 0, 0, 0.957926f, -0.287016f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_FLAG_MAGE_TOWER, BG_OBJECT_FLAG1_EY_ENTRY, 2284.48f, 1731.23f, 1189.99f, 2.89725f, 0, 0, 0.992546f, 0.121869f, RESPAWN_ONE_DAY) + // tower cap + || !AddObject(BG_EY_OBJECT_TOWER_CAP_FEL_REALVER, BG_OBJECT_FR_TOWER_CAP_EY_ENTRY, 2024.600708f, 1742.819580f, 1195.157715f, 2.443461f, 0, 0, 0.939693f, 0.342020f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_TOWER_CAP_BLOOD_ELF, BG_OBJECT_BE_TOWER_CAP_EY_ENTRY, 2050.493164f, 1372.235962f, 1194.563477f, 1.710423f, 0, 0, 0.754710f, 0.656059f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_TOWER_CAP_DRAENEI_RUINS, BG_OBJECT_DR_TOWER_CAP_EY_ENTRY, 2301.010498f, 1386.931641f, 1197.183472f, 1.570796f, 0, 0, 0.707107f, 0.707107f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_TOWER_CAP_MAGE_TOWER, BG_OBJECT_HU_TOWER_CAP_EY_ENTRY, 2282.121582f, 1760.006958f, 1189.707153f, 1.919862f, 0, 0, 0.819152f, 0.573576f, RESPAWN_ONE_DAY) + ) + { + sLog.outErrorDb("BatteGroundEY: Failed to spawn some object BattleGround not created!"); + return false; + } + + //buffs + for (int i = 0; i < EY_POINTS_MAX; ++i) + { + AreaTriggerEntry const* at = sAreaTriggerStore.LookupEntry(m_Points_Trigger[i]); + if( !at ) + { + sLog.outError("BattleGroundEY: Unknown trigger: %u", m_Points_Trigger[i]); + continue; + } + if ( !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3, Buff_Entries[0], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3 + 1, Buff_Entries[1], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) + || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + i * 3 + 2, Buff_Entries[2], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) + ) + sLog.outError("BattleGroundEY: Cannot spawn buff"); + } + + WorldSafeLocsEntry const *sg = NULL; + sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE); + if( !sg || !AddSpiritGuide(EY_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE) ) + { + sLog.outErrorDb("BatteGroundEY: Failed to spawn spirit guide! BattleGround not created!"); + return false; + } + + sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_HORDE); + if( !sg || !AddSpiritGuide(EY_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE) ) + { + sLog.outErrorDb("BatteGroundEY: Failed to spawn spirit guide! BattleGround not created!"); + return false; + } + + return true; +} + +void BattleGroundEY::ResetBGSubclass() +{ + m_TeamScores[BG_TEAM_ALLIANCE] = 0; + m_TeamScores[BG_TEAM_HORDE] = 0; + m_TeamPointsCount[BG_TEAM_ALLIANCE] = 0; + m_TeamPointsCount[BG_TEAM_HORDE] = 0; + m_HonorScoreTics[BG_TEAM_ALLIANCE] = 0; + m_HonorScoreTics[BG_TEAM_HORDE] = 0; + m_FlagState = BG_EY_FLAG_STATE_ON_BASE; + m_FlagCapturedBgObjectType = 0; + m_FlagKeeper = 0; + m_DroppedFlagGUID = 0; + m_PointAddingTimer = 0; + m_TowerCapCheckTimer = 0; + + for(uint8 i = 0; i < EY_POINTS_MAX; ++i) + { + m_PointOwnedByTeam[i] = EY_POINT_NO_OWNER; + m_PointState[i] = EY_POINT_STATE_UNCONTROLLED; + m_PointBarStatus[i] = BG_EY_PROGRESS_BAR_STATE_MIDDLE; + m_PlayersNearPoint[i].clear(); + m_PlayersNearPoint[i].reserve(15); //tip size + } + m_PlayersNearPoint[EY_PLAYERS_OUT_OF_POINTS].clear(); + m_PlayersNearPoint[EY_PLAYERS_OUT_OF_POINTS].reserve(30); +} + +void BattleGroundEY::RespawnFlag(bool send_message) +{ + if (m_FlagCapturedBgObjectType > 0) + SpawnBGObject(m_FlagCapturedBgObjectType, RESPAWN_ONE_DAY); + + m_FlagCapturedBgObjectType = 0; + m_FlagState = BG_EY_FLAG_STATE_ON_BASE; + SpawnBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM, RESPAWN_IMMEDIATELY); + + if(send_message) + { + SendMessageToAll(GetMangosString(LANG_BG_EY_RESETED_FLAG)); + PlaySoundToAll(BG_EY_SOUND_FLAG_RESET); // flags respawned sound... + } + + UpdateWorldState(NETHERSTORM_FLAG, 1); +} + +void BattleGroundEY::RespawnFlagAfterDrop() +{ + RespawnFlag(true); + + GameObject *obj = HashMapHolder::Find(GetDroppedFlagGUID()); + if(obj) + obj->Delete(); + else + sLog.outError("BattleGroundEY: Unknown dropped flag guid: %u",GetDroppedFlagGUID()); + + SetDroppedFlagGUID(0); +} + +void BattleGroundEY::HandleKillPlayer(Player *player, Player *killer) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + BattleGround::HandleKillPlayer(player, killer); + EventPlayerDroppedFlag(player); +} + +void BattleGroundEY::EventPlayerDroppedFlag(Player *Source) +{ + // Drop allowed in any BG state + + if(!IsFlagPickedup()) + return; + + if(GetFlagPickerGUID() != Source->GetGUID()) + return; + + const char *message = ""; + uint8 type = 0; + + SetFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); + m_FlagState = BG_EY_FLAG_STATE_ON_GROUND; + m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME; + Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true); + Source->CastSpell(Source, BG_EY_PLAYER_DROPPED_FLAG_SPELL, true); + if(Source->GetTeam() == ALLIANCE) + { + message = GetMangosString(LANG_BG_EY_DROPPED_FLAG); + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + } + else + { + message = GetMangosString(LANG_BG_EY_DROPPED_FLAG); + type = CHAT_MSG_BG_SYSTEM_HORDE; + } + //this does not work correctly :( (it should remove flag carrier name) + UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_WAIT_RESPAWN); + UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_WAIT_RESPAWN); + + WorldPacket data; + ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); + SendPacketToAll(&data); +} + +void BattleGroundEY::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) +{ + if(GetStatus() != STATUS_IN_PROGRESS || this->IsFlagPickedup() || !Source->IsWithinDistInMap(target_obj, 10)) + return; + + const char *message; + uint8 type = 0; + message = GetMangosString(LANG_BG_EY_HAS_TAKEN_FLAG); + + if(Source->GetTeam() == ALLIANCE) + { + UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_ON_PLAYER); + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE); + } + else + { + UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_ON_PLAYER); + type = CHAT_MSG_BG_SYSTEM_HORDE; + PlaySoundToAll(BG_EY_SOUND_FLAG_PICKED_UP_HORDE); + } + + if (m_FlagState == BG_EY_FLAG_STATE_ON_BASE) + UpdateWorldState(NETHERSTORM_FLAG, 0); + m_FlagState = BG_EY_FLAG_STATE_ON_PLAYER; + + SpawnBGObject(BG_EY_OBJECT_FLAG_NETHERSTORM, RESPAWN_ONE_DAY); + SetFlagPicker(Source->GetGUID()); + //get flag aura on player + Source->CastSpell(Source, BG_EY_NETHERSTORM_FLAG_SPELL, true); + Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); + + WorldPacket data; + ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); + SendPacketToAll(&data); +} + +void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + //Natural point + uint8 message_type = 0; + const char *message = ""; + uint32 Team = m_PointOwnedByTeam[Point]; + + if(!Team) + return; + + if (Team == ALLIANCE) + { + m_TeamPointsCount[BG_TEAM_ALLIANCE]--; + message_type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + message = GetMangosString(m_LoosingPointTypes[Point].MessageIdAlliance); + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance, RESPAWN_ONE_DAY); + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance + 1, RESPAWN_ONE_DAY); + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeAlliance + 2, RESPAWN_ONE_DAY); + } + else + { + m_TeamPointsCount[BG_TEAM_HORDE]--; + message_type = CHAT_MSG_BG_SYSTEM_HORDE; + message = GetMangosString(m_LoosingPointTypes[Point].MessageIdHorde); + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde, RESPAWN_ONE_DAY); + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde + 1, RESPAWN_ONE_DAY); + SpawnBGObject(m_LoosingPointTypes[Point].DespawnObjectTypeHorde + 2, RESPAWN_ONE_DAY); + } + + SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType + 1, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_LoosingPointTypes[Point].SpawnNeutralObjectType + 2, RESPAWN_IMMEDIATELY); + + //buff isn't despawned + + m_PointOwnedByTeam[Point] = EY_POINT_NO_OWNER; + m_PointState[Point] = EY_POINT_NO_OWNER; + + WorldPacket data; + ChatHandler::FillMessageData(&data, Source->GetSession(), message_type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); + SendPacketToAll(&data); + + UpdatePointsIcons(Team, Point); + UpdatePointsCount(Team); +} + +void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + uint8 type = 0; + const char *message = ""; + uint32 Team = Source->GetTeam(); + + SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType, RESPAWN_ONE_DAY); + SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType + 1, RESPAWN_ONE_DAY); + SpawnBGObject(m_CapturingPointTypes[Point].DespawnNeutralObjectType + 2, RESPAWN_ONE_DAY); + + if (Team == ALLIANCE) + { + m_TeamPointsCount[BG_TEAM_ALLIANCE]++; + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + message = GetMangosString(m_CapturingPointTypes[Point].MessageIdAlliance); + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance + 1, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeAlliance + 2, RESPAWN_IMMEDIATELY); + } + else + { + m_TeamPointsCount[BG_TEAM_HORDE]++; + type = CHAT_MSG_BG_SYSTEM_HORDE; + message = GetMangosString(m_CapturingPointTypes[Point].MessageIdHorde); + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde + 1, RESPAWN_IMMEDIATELY); + SpawnBGObject(m_CapturingPointTypes[Point].SpawnObjectTypeHorde + 2, RESPAWN_IMMEDIATELY); + } + + //buff isn't respawned + + m_PointOwnedByTeam[Point] = Team; + m_PointState[Point] = EY_POINT_UNDER_CONTROL; + + WorldPacket data; + ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); + SendPacketToAll(&data); + + if(m_BgCreatures[Point]) + DelCreature(Point); + + WorldSafeLocsEntry const *sg = NULL; + sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveYardId); + if(!sg || !AddSpiritGuide(Point, sg->x, sg->y, sg->z, 3.124139f, Team)) + sLog.outError("BatteGroundEY: Failed to spawn spirit guide! point: %u, team: u, graveyard_id: %u", Point, Team, m_CapturingPointTypes[Point].GraveYardId); + + UpdatePointsIcons(Team, Point); + UpdatePointsCount(Team); +} + +void BattleGroundEY::EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType) +{ + if(GetStatus() != STATUS_IN_PROGRESS || this->GetFlagPickerGUID() != Source->GetGUID()) + return; + + uint8 type = 0; + uint8 team_id = 0; + const char *message = ""; + + SetFlagPicker(0); + m_FlagState = BG_EY_FLAG_STATE_WAIT_RESPAWN; + Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL); + + Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); + if(Source->GetTeam() == ALLIANCE) + { + PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE); + team_id = BG_TEAM_ALLIANCE; + message = GetMangosString(LANG_BG_EY_CAPTURED_FLAG_A); + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + } + else + { + PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_HORDE); + team_id = BG_TEAM_HORDE; + message = GetMangosString(LANG_BG_EY_CAPTURED_FLAG_H); + type = CHAT_MSG_BG_SYSTEM_HORDE; + } + + SpawnBGObject(BgObjectType, RESPAWN_IMMEDIATELY); + + m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME; + m_FlagCapturedBgObjectType = BgObjectType; + + WorldPacket data; + ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); + SendPacketToAll(&data); + + if(m_TeamPointsCount[team_id] > 0) + AddPoints(Source->GetTeam(), BG_EY_FlagPoints[m_TeamPointsCount[team_id] - 1]); + + UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1); +} + +void BattleGroundEY::UpdatePlayerScore(Player *Source, uint32 type, uint32 value) +{ + std::map::iterator itr = m_PlayerScores.find(Source->GetGUID()); + + if(itr == m_PlayerScores.end()) // player not found + return; + + switch(type) + { + case SCORE_FLAG_CAPTURES: // flags captured + ((BattleGroundEYScore*)itr->second)->FlagCaptures += value; + break; + default: + BattleGround::UpdatePlayerScore(Source, type, value); + break; + } +} + +void BattleGroundEY::FillInitialWorldStates(WorldPacket& data) +{ + data << uint32(EY_HORDE_BASE) << uint32(m_TeamPointsCount[BG_TEAM_HORDE]); + data << uint32(EY_ALLIANCE_BASE) << uint32(m_TeamPointsCount[BG_TEAM_ALLIANCE]); + data << uint32(0xab6) << uint32(0x0); + data << uint32(0xab5) << uint32(0x0); + data << uint32(0xab4) << uint32(0x0); + data << uint32(0xab3) << uint32(0x0); + data << uint32(0xab2) << uint32(0x0); + data << uint32(0xab1) << uint32(0x0); + data << uint32(0xab0) << uint32(0x0); + data << uint32(0xaaf) << uint32(0x0); + + data << uint32(DRAENEI_RUINS_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[DRAENEI_RUINS] == HORDE && m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL); + + data << uint32(DRAENEI_RUINS_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[DRAENEI_RUINS] == ALLIANCE && m_PointState[DRAENEI_RUINS] == EY_POINT_UNDER_CONTROL); + + data << uint32(DRAENEI_RUINS_UNCONTROL) << uint32(m_PointState[DRAENEI_RUINS] != EY_POINT_UNDER_CONTROL); + + data << uint32(MAGE_TOWER_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[MAGE_TOWER] == ALLIANCE && m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL); + + data << uint32(MAGE_TOWER_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[MAGE_TOWER] == HORDE && m_PointState[MAGE_TOWER] == EY_POINT_UNDER_CONTROL); + + data << uint32(MAGE_TOWER_UNCONTROL) << uint32(m_PointState[MAGE_TOWER] != EY_POINT_UNDER_CONTROL); + + data << uint32(FEL_REAVER_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[FEL_REALVER] == HORDE && m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL); + + data << uint32(FEL_REAVER_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[FEL_REALVER] == ALLIANCE && m_PointState[FEL_REALVER] == EY_POINT_UNDER_CONTROL); + + data << uint32(FEL_REAVER_UNCONTROL) << uint32(m_PointState[FEL_REALVER] != EY_POINT_UNDER_CONTROL); + + data << uint32(BLOOD_ELF_HORDE_CONTROL) << uint32(m_PointOwnedByTeam[BLOOD_ELF] == HORDE && m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL); + + data << uint32(BLOOD_ELF_ALLIANCE_CONTROL) << uint32(m_PointOwnedByTeam[BLOOD_ELF] == ALLIANCE && m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL); + + data << uint32(BLOOD_ELF_UNCONTROL) << uint32(m_PointState[BLOOD_ELF] != EY_POINT_UNDER_CONTROL); + + data << uint32(NETHERSTORM_FLAG) << uint32(m_FlagState == BG_EY_FLAG_STATE_ON_BASE); + + data << uint32(0xad2) << uint32(0x1); + data << uint32(0xad1) << uint32(0x1); + data << uint32(0xabe) << uint32(GetTeamScore(HORDE)); + data << uint32(0xabd) << uint32(GetTeamScore(ALLIANCE)); + data << uint32(0xa05) << uint32(0x8e); + data << uint32(0xaa0) << uint32(0x0); + data << uint32(0xa9f) << uint32(0x0); + data << uint32(0xa9e) << uint32(0x0); + data << uint32(0xc0d) << uint32(0x17b); +} + +WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(float x, float y, float z, uint32 team) +{ + uint32 g_id = 0; + + if(team == ALLIANCE) + g_id = EY_GRAVEYARD_MAIN_ALLIANCE; + else if(team == HORDE) + g_id = EY_GRAVEYARD_MAIN_HORDE; + else + return NULL; + + float distance, nearestDistance; + + WorldSafeLocsEntry const* entry = NULL; + WorldSafeLocsEntry const* nearestEntry = NULL; + entry = sWorldSafeLocsStore.LookupEntry(g_id); + nearestEntry = entry; + + if(!entry) + { + sLog.outError("BattleGroundEY: Not found the main team graveyard. Graveyard system isn't working!"); + return NULL; + } + + distance = (entry->x - x)*(entry->x - x) + (entry->y - y)*(entry->y - y) + (entry->z - z)*(entry->z - z); + nearestDistance = distance; + + for(uint8 i = 0; i < EY_POINTS_MAX; ++i) + { + if(m_PointOwnedByTeam[i]==team && m_PointState[i]==EY_POINT_UNDER_CONTROL) + { + entry = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[i].GraveYardId); + if(!entry) + sLog.outError("BattleGroundEY: Not found graveyard: %u",m_CapturingPointTypes[i].GraveYardId); + else + { + distance = (entry->x - x)*(entry->x - x) + (entry->y - y)*(entry->y - y) + (entry->z - z)*(entry->z - z); + if(distance < nearestDistance) + { + nearestDistance = distance; + nearestEntry = entry; + } + } + } + } + + return nearestEntry; +} diff --git a/src/game/BattleGroundEY.h b/src/game/BattleGroundEY.h new file mode 100644 index 00000000000..661f107682d --- /dev/null +++ b/src/game/BattleGroundEY.h @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDEY_H +#define __BATTLEGROUNDEY_H + +#include "Language.h" + +class BattleGround; + +#define EY_MAX_TEAM_SCORE 2000 +#define BG_EY_FLAG_RESPAWN_TIME 10000 //10 seconds +#define BG_EY_FPOINTS_TICK_TIME 2000 //2 seconds + +enum BG_EY_WorldStates +{ + EY_ALLIANCE_RESOURCES = 2749, + EY_HORDE_RESOURCES = 2750, + EY_ALLIANCE_BASE = 2752, + EY_HORDE_BASE = 2753, + DRAENEI_RUINS_HORDE_CONTROL = 2733, + DRAENEI_RUINS_ALLIANCE_CONTROL = 2732, + DRAENEI_RUINS_UNCONTROL = 2731, + MAGE_TOWER_ALLIANCE_CONTROL = 2730, + MAGE_TOWER_HORDE_CONTROL = 2729, + MAGE_TOWER_UNCONTROL = 2728, + FEL_REAVER_HORDE_CONTROL = 2727, + FEL_REAVER_ALLIANCE_CONTROL = 2726, + FEL_REAVER_UNCONTROL = 2725, + BLOOD_ELF_HORDE_CONTROL = 2724, + BLOOD_ELF_ALLIANCE_CONTROL = 2723, + BLOOD_ELF_UNCONTROL = 2722, + PROGRESS_BAR_PERCENT_GREY = 2720, //100 = empty (only grey), 0 = blue|red (no grey) + PROGRESS_BAR_STATUS = 2719, //50 init!, 48 ... hordak bere .. 33 .. 0 = full 100% hordacky , 100 = full alliance + PROGRESS_BAR_SHOW = 2718, //1 init, 0 druhy send - bez messagu, 1 = controlled aliance + NETHERSTORM_FLAG = 2757, + //set to 2 when flag is picked up, and to 1 if it is dropped + NETHERSTORM_FLAG_STATE_ALLIANCE = 2769, + NETHERSTORM_FLAG_STATE_HORDE = 2770 +}; + +enum BG_EY_ProgressBarConsts +{ + BG_EY_POINT_MAX_CAPTURERS_COUNT = 5, + BG_EY_POINT_RADIUS = 70, + BG_EY_PROGRESS_BAR_DONT_SHOW = 0, + BG_EY_PROGRESS_BAR_SHOW = 1, + BG_EY_PROGRESS_BAR_PERCENT_GREY = 40, + BG_EY_PROGRESS_BAR_STATE_MIDDLE = 50, + BG_EY_PROGRESS_BAR_HORDE_CONTROLLED = 0, + BG_EY_PROGRESS_BAR_NEUTRAL_LOW = 30, + BG_EY_PROGRESS_BAR_NEUTRAL_HIGH = 70, + BG_EY_PROGRESS_BAR_ALI_CONTROLLED = 100 +}; + +enum BG_EY_Sounds +{ + //strange ids, but sure about them + BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE = 8212, + BG_EY_SOUND_FLAG_CAPTURED_HORDE = 8213, + BG_EY_SOUND_FLAG_PICKED_UP_HORDE = 8174, + BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, + BG_EY_SOUND_FLAG_RESET = 8192 +}; + +enum BG_EY_Spells +{ + BG_EY_NETHERSTORM_FLAG_SPELL = 34976, + BG_EY_PLAYER_DROPPED_FLAG_SPELL = 34991 +}; + +enum EYBattleGroundObjectEntry +{ + BG_OBJECT_A_DOOR_EY_ENTRY = 184719, //Alliance door + BG_OBJECT_H_DOOR_EY_ENTRY = 184720, //Horde door + BG_OBJECT_FLAG1_EY_ENTRY = 184493, //Netherstorm flag (generic) + BG_OBJECT_FLAG2_EY_ENTRY = 184141, //Netherstorm flag (flagstand) + BG_OBJECT_FLAG3_EY_ENTRY = 184142, //Netherstorm flag (flagdrop) + BG_OBJECT_A_BANNER_EY_ENTRY = 184381, //Visual Banner (Alliance) + BG_OBJECT_H_BANNER_EY_ENTRY = 184380, //Visual Banner (Horde) + BG_OBJECT_N_BANNER_EY_ENTRY = 184382, //Visual Banner (Neutral) + BG_OBJECT_BE_TOWER_CAP_EY_ENTRY = 184080, //BE Tower Cap Pt + BG_OBJECT_FR_TOWER_CAP_EY_ENTRY = 184081, //Fel Reaver Cap Pt + BG_OBJECT_HU_TOWER_CAP_EY_ENTRY = 184082, //Human Tower Cap Pt + BG_OBJECT_DR_TOWER_CAP_EY_ENTRY = 184083 //Draenei Tower Cap Pt +}; + +enum EYBattleGroundPointsTrigger +{ + TR_BLOOD_ELF_POINT = 4476, + TR_FEL_REALVER_POINT = 4514, + TR_MAGE_TOWER_POINT = 4516, + TR_DRAENEI_RUINS_POINT = 4518, + TR_BLOOD_ELF_BUFF = 4568, + TR_FEL_REALVER_BUFF = 4569, + TR_MAGE_TOWER_BUFF = 4570, + TR_DRAENEI_RUINS_BUFF = 4571 +}; + +enum EYBattleGroundGaveyards +{ + EY_GRAVEYARD_MAIN_ALLIANCE = 1103, + EY_GRAVEYARD_MAIN_HORDE = 1104, + EY_GRAVEYARD_FEL_REALVER = 1105, + EY_GRAVEYARD_BLOOD_ELF = 1106, + EY_GRAVEYARD_DRAENEI_RUINS = 1107, + EY_GRAVEYARD_MAGE_TOWER = 1108 +}; + +enum EYBattleGroundPoints +{ + FEL_REALVER = 0, + BLOOD_ELF = 1, + DRAENEI_RUINS = 2, + MAGE_TOWER = 3, + + EY_PLAYERS_OUT_OF_POINTS = 4, + EY_POINTS_MAX = 4 +}; + +enum EYBattleGroundCreaturesTypes +{ + EY_SPIRIT_FEL_REALVER = 0, + EY_SPIRIT_BLOOD_ELF = 1, + EY_SPIRIT_DRAENEI_RUINS = 2, + EY_SPIRIT_MAGE_TOWER = 3, + EY_SPIRIT_MAIN_ALLIANCE = 4, + EY_SPIRIT_MAIN_HORDE = 5, + + BG_EY_CREATURES_MAX = 6 +}; + +enum EYBattleGroundObjectTypes +{ + BG_EY_OBJECT_DOOR_A = 0, + BG_EY_OBJECT_DOOR_H = 1, + BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER = 2, + BG_EY_OBJECT_A_BANNER_FEL_REALVER_LEFT = 3, + BG_EY_OBJECT_A_BANNER_FEL_REALVER_RIGHT = 4, + BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER = 5, + BG_EY_OBJECT_A_BANNER_BLOOD_ELF_LEFT = 6, + BG_EY_OBJECT_A_BANNER_BLOOD_ELF_RIGHT = 7, + BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER = 8, + BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_LEFT = 9, + BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_RIGHT = 10, + BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER = 11, + BG_EY_OBJECT_A_BANNER_MAGE_TOWER_LEFT = 12, + BG_EY_OBJECT_A_BANNER_MAGE_TOWER_RIGHT = 13, + BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER = 14, + BG_EY_OBJECT_H_BANNER_FEL_REALVER_LEFT = 15, + BG_EY_OBJECT_H_BANNER_FEL_REALVER_RIGHT = 16, + BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER = 17, + BG_EY_OBJECT_H_BANNER_BLOOD_ELF_LEFT = 18, + BG_EY_OBJECT_H_BANNER_BLOOD_ELF_RIGHT = 19, + BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER = 20, + BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_LEFT = 21, + BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_RIGHT = 22, + BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER = 23, + BG_EY_OBJECT_H_BANNER_MAGE_TOWER_LEFT = 24, + BG_EY_OBJECT_H_BANNER_MAGE_TOWER_RIGHT = 25, + BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER = 26, + BG_EY_OBJECT_N_BANNER_FEL_REALVER_LEFT = 27, + BG_EY_OBJECT_N_BANNER_FEL_REALVER_RIGHT = 28, + BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER = 29, + BG_EY_OBJECT_N_BANNER_BLOOD_ELF_LEFT = 30, + BG_EY_OBJECT_N_BANNER_BLOOD_ELF_RIGHT = 31, + BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER = 32, + BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_LEFT = 33, + BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_RIGHT = 34, + BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER = 35, + BG_EY_OBJECT_N_BANNER_MAGE_TOWER_LEFT = 36, + BG_EY_OBJECT_N_BANNER_MAGE_TOWER_RIGHT = 37, + BG_EY_OBJECT_TOWER_CAP_FEL_REALVER = 38, + BG_EY_OBJECT_TOWER_CAP_BLOOD_ELF = 39, + BG_EY_OBJECT_TOWER_CAP_DRAENEI_RUINS = 40, + BG_EY_OBJECT_TOWER_CAP_MAGE_TOWER = 41, + BG_EY_OBJECT_FLAG_NETHERSTORM = 42, + BG_EY_OBJECT_FLAG_FEL_REALVER = 43, + BG_EY_OBJECT_FLAG_BLOOD_ELF = 44, + BG_EY_OBJECT_FLAG_DRAENEI_RUINS = 45, + BG_EY_OBJECT_FLAG_MAGE_TOWER = 46, + //buffs + BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER = 47, + BG_EY_OBJECT_REGENBUFF_FEL_REALVER = 48, + BG_EY_OBJECT_BERSERKBUFF_FEL_REALVER = 49, + BG_EY_OBJECT_SPEEDBUFF_BLOOD_ELF = 50, + BG_EY_OBJECT_REGENBUFF_BLOOD_ELF = 51, + BG_EY_OBJECT_BERSERKBUFF_BLOOD_ELF = 52, + BG_EY_OBJECT_SPEEDBUFF_DRAENEI_RUINS = 53, + BG_EY_OBJECT_REGENBUFF_DRAENEI_RUINS = 54, + BG_EY_OBJECT_BERSERKBUFF_DRAENEI_RUINS = 55, + BG_EY_OBJECT_SPEEDBUFF_MAGE_TOWER = 56, + BG_EY_OBJECT_REGENBUFF_MAGE_TOWER = 57, + BG_EY_OBJECT_BERSERKBUFF_MAGE_TOWER = 58, + BG_EY_OBJECT_MAX = 59 +}; + +enum BG_EY_FlagState +{ + BG_EY_FLAG_STATE_ON_BASE = 0, + BG_EY_FLAG_STATE_WAIT_RESPAWN = 1, + BG_EY_FLAG_STATE_ON_PLAYER = 2, + BG_EY_FLAG_STATE_ON_GROUND = 3 +}; + +enum EYBattleGroundPointState +{ + EY_POINT_NO_OWNER = 0, + EY_POINT_STATE_UNCONTROLLED = 0, + EY_POINT_UNDER_CONTROL = 3 +}; + +struct BattleGroundEYPointIconsStruct +{ + BattleGroundEYPointIconsStruct(uint32 _WorldStateControlIndex, uint32 _WorldStateAllianceControlledIndex, uint32 _WorldStateHordeControlledIndex) + : WorldStateControlIndex(_WorldStateControlIndex), WorldStateAllianceControlledIndex(_WorldStateAllianceControlledIndex), WorldStateHordeControlledIndex(_WorldStateHordeControlledIndex) {} + uint32 WorldStateControlIndex; + uint32 WorldStateAllianceControlledIndex; + uint32 WorldStateHordeControlledIndex; +}; + +struct BattleGroundEYLoosingPointStruct +{ + BattleGroundEYLoosingPointStruct(uint32 _SpawnNeutralObjectType, uint32 _DespawnObjectTypeAlliance, uint32 _MessageIdAlliance, uint32 _DespawnObjectTypeHorde, uint32 _MessageIdHorde) + : SpawnNeutralObjectType(_SpawnNeutralObjectType), DespawnObjectTypeAlliance(_DespawnObjectTypeAlliance), MessageIdAlliance(_MessageIdAlliance), DespawnObjectTypeHorde(_DespawnObjectTypeHorde), MessageIdHorde(_MessageIdHorde) {} + uint32 SpawnNeutralObjectType; + uint32 DespawnObjectTypeAlliance; + uint32 DespawnObjectTypeHorde; + uint32 MessageIdHorde; + uint32 MessageIdAlliance; +}; + +struct BattleGroundEYCapturingPointStruct +{ + BattleGroundEYCapturingPointStruct(uint32 _DespawnNeutralObjectType, uint32 _SpawnObjectTypeAlliance, uint32 _MessageIdAlliance, uint32 _SpawnObjectTypeHorde, uint32 _MessageIdHorde, uint32 _GraveYardId) + : DespawnNeutralObjectType(_DespawnNeutralObjectType), SpawnObjectTypeAlliance(_SpawnObjectTypeAlliance), MessageIdAlliance(_MessageIdAlliance), SpawnObjectTypeHorde(_SpawnObjectTypeHorde), MessageIdHorde(_MessageIdHorde), GraveYardId(_GraveYardId) {} + uint32 DespawnNeutralObjectType; + uint32 SpawnObjectTypeAlliance; + uint32 SpawnObjectTypeHorde; + uint32 MessageIdHorde; + uint32 MessageIdAlliance; + uint32 GraveYardId; +}; + +const uint8 BG_EY_TickPoints[EY_POINTS_MAX] = {1, 2, 5, 10}; +const uint32 BG_EY_FlagPoints[EY_POINTS_MAX] = {75, 85, 100, 500}; + +//constant arrays: +const BattleGroundEYPointIconsStruct m_PointsIconStruct[EY_POINTS_MAX] = +{ + BattleGroundEYPointIconsStruct(FEL_REAVER_UNCONTROL, FEL_REAVER_ALLIANCE_CONTROL, FEL_REAVER_HORDE_CONTROL), + BattleGroundEYPointIconsStruct(BLOOD_ELF_UNCONTROL, BLOOD_ELF_ALLIANCE_CONTROL, BLOOD_ELF_HORDE_CONTROL), + BattleGroundEYPointIconsStruct(DRAENEI_RUINS_UNCONTROL, DRAENEI_RUINS_ALLIANCE_CONTROL, DRAENEI_RUINS_HORDE_CONTROL), + BattleGroundEYPointIconsStruct(MAGE_TOWER_UNCONTROL, MAGE_TOWER_ALLIANCE_CONTROL, MAGE_TOWER_HORDE_CONTROL) +}; +const BattleGroundEYLoosingPointStruct m_LoosingPointTypes[EY_POINTS_MAX] = +{ + BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_LOST_A_F_RUINS, BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_LOST_H_F_RUINS), + BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_LOST_A_B_TOWER, BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_LOST_H_B_TOWER), + BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_LOST_A_D_RUINS, BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_LOST_H_D_RUINS), + BattleGroundEYLoosingPointStruct(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_LOST_A_M_TOWER, BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_LOST_H_M_TOWER) +}; +const BattleGroundEYCapturingPointStruct m_CapturingPointTypes[EY_POINTS_MAX] = +{ + BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_FEL_REALVER_CENTER, BG_EY_OBJECT_A_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_TAKEN_A_F_RUINS, BG_EY_OBJECT_H_BANNER_FEL_REALVER_CENTER, LANG_BG_EY_HAS_TAKEN_H_F_RUINS, EY_GRAVEYARD_FEL_REALVER), + BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_BLOOD_ELF_CENTER, BG_EY_OBJECT_A_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_TAKEN_A_B_TOWER, BG_EY_OBJECT_H_BANNER_BLOOD_ELF_CENTER, LANG_BG_EY_HAS_TAKEN_H_B_TOWER, EY_GRAVEYARD_BLOOD_ELF), + BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_DRAENEI_RUINS_CENTER, BG_EY_OBJECT_A_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_TAKEN_A_D_RUINS, BG_EY_OBJECT_H_BANNER_DRAENEI_RUINS_CENTER, LANG_BG_EY_HAS_TAKEN_H_D_RUINS, EY_GRAVEYARD_DRAENEI_RUINS), + BattleGroundEYCapturingPointStruct(BG_EY_OBJECT_N_BANNER_MAGE_TOWER_CENTER, BG_EY_OBJECT_A_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_TAKEN_A_M_TOWER, BG_EY_OBJECT_H_BANNER_MAGE_TOWER_CENTER, LANG_BG_EY_HAS_TAKEN_H_M_TOWER, EY_GRAVEYARD_MAGE_TOWER) +}; + +class BattleGroundEYScore : public BattleGroundScore +{ + public: + BattleGroundEYScore () : FlagCaptures(0) {}; + virtual ~BattleGroundEYScore() {}; + uint32 FlagCaptures; +}; + +class BattleGroundEY : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundEY(); + ~BattleGroundEY(); + void Update(time_t diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + + /* BG Flags */ + uint64 GetFlagPickerGUID() const { return m_FlagKeeper; } + void SetFlagPicker(uint64 guid) { m_FlagKeeper = guid; } + bool IsFlagPickedup() const { return m_FlagKeeper != 0; } + uint8 GetFlagState() const { return m_FlagState; } + void RespawnFlag(bool send_message); + void RespawnFlagAfterDrop(); + + void RemovePlayer(Player *plr,uint64 guid); + void HandleBuffUse(uint64 const& buff_guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + void HandleKillPlayer(Player *player, Player *killer); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(float x, float y, float z, uint32 team); + virtual bool SetupBattleGround(); + virtual void ResetBGSubclass(); + void UpdateTeamScore(uint32 Team); + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); + virtual void FillInitialWorldStates(WorldPacket& data); + void SetDroppedFlagGUID(uint64 guid) { m_DroppedFlagGUID = guid;} + uint64 GetDroppedFlagGUID() const { return m_DroppedFlagGUID;} + + /* Battleground Events */ + virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj); + virtual void EventPlayerDroppedFlag(Player *Source); + + private: + void EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType); + void EventTeamCapturedPoint(Player *Source, uint32 Point); + void EventTeamLostPoint(Player *Source, uint32 Point); + void UpdatePointsCount(uint32 Team); + void UpdatePointsIcons(uint32 Team, uint32 Point); + + /* Point status updating procedures */ + void CheckSomeoneLeftPoint(); + void CheckSomeoneJoinedPoint(); + void UpdatePointStatuses(); + + /* Scorekeeping */ + uint32 GetTeamScore(uint32 Team) const { return m_TeamScores[GetTeamIndexByTeamId(Team)]; } + void AddPoints(uint32 Team, uint32 Points); + + void RemovePoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] -= Points; } + void SetTeamPoint(uint32 TeamID, uint32 Points = 0) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] = Points; } + + uint32 m_TeamScores[2]; + uint32 m_HonorScoreTics[2]; + uint32 m_TeamPointsCount[2]; + + uint32 m_Points_Trigger[EY_POINTS_MAX]; + + uint64 m_FlagKeeper; // keepers guid + uint64 m_DroppedFlagGUID; + uint32 m_FlagCapturedBgObjectType; // type that should be despawned when flag is captured + uint8 m_FlagState; // for checking flag state + int32 m_FlagsTimer; + int32 m_TowerCapCheckTimer; + + uint32 m_PointOwnedByTeam[EY_POINTS_MAX]; + uint8 m_PointState[EY_POINTS_MAX]; + int32 m_PointBarStatus[EY_POINTS_MAX]; + typedef std::vector PlayersNearPointType; + PlayersNearPointType m_PlayersNearPoint[EY_POINTS_MAX + 1]; + uint8 m_CurrentPointPlayersCount[2*EY_POINTS_MAX]; + + int32 m_PointAddingTimer; +}; +#endif diff --git a/src/game/BattleGroundHandler.cpp b/src/game/BattleGroundHandler.cpp new file mode 100644 index 00000000000..85de6855563 --- /dev/null +++ b/src/game/BattleGroundHandler.cpp @@ -0,0 +1,621 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 "BattleGroundMgr.h" +#include "BattleGroundWS.h" +#include "BattleGround.h" + +void WorldSession::HandleBattleGroundHelloOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + uint64 guid; + recv_data >> guid; + sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_HELLO Message from: " I64FMT, guid); + + Creature *unit = ObjectAccessor::GetCreature(*_player, guid); + if(!unit) + return; + + if(!unit->isBattleMaster()) // it's not battlemaster + return; + + // Stop the npc if moving + unit->StopMoving(); + + uint32 bgTypeId = objmgr.GetBattleMasterBG(unit->GetEntry()); + + if(!_player->GetBGAccessByLevel(bgTypeId)) + { + // temp, must be gossip message... + SendNotification("You don't meet Battleground level requirements"); + return; + } + + SendBattlegGroundList(guid, bgTypeId); +} + +void WorldSession::SendBattlegGroundList( uint64 guid, uint32 bgTypeId ) +{ + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundListPacket(&data, guid, _player, bgTypeId); + SendPacket( &data ); +} + +void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 8+4+4+1); + + uint64 guid; + uint32 bgTypeId; + uint32 instanceId; + uint8 joinAsGroup; + + 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 + + sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from: " I64FMT " for BG (Type: %u)", guid, bgTypeId); + + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? + return; + + // 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; + + // check Deserter debuff + if( !_player->CanJoinToBattleground() ) + { + WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4); + data << (uint32) 0xFFFFFFFE; + _player->GetSession()->SendPacket(&data); + return; + } + + // check existence + BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); + if(!bg) + return; + + if(joinAsGroup && _player->GetGroup()) + { + Group *grp = _player->GetGroup(); + for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + if(!member) continue; + + if( !member->CanJoinToBattleground() ) + { + WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4); + data << (uint32) 0xFFFFFFFE; + _player->GetSession()->SendPacket(&data); + continue; + } + if (member->InBattleGroundQueueForBattleGroundType(bgTypeId)) + //player is already in this queue + continue; + + WorldPacket data; + // add to queue + uint32 queueSlot = member->AddBattleGroundQueueId(bgTypeId); + if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) + { + // fill data packet + //member->GetSession()->SendPacket(data); + continue; + } + + // store entry point coords (same as leader entry point) + member->SetBattleGroundEntryPoint(_player->GetMapId(),_player->GetPositionX(),_player->GetPositionY(),_player->GetPositionZ(),_player->GetOrientation()); + + // 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[bgTypeId].AddPlayer(member, bgTypeId); + } + } + else + { + if (_player->InBattleGroundQueueForBattleGroundType(bgTypeId)) + //player is already in this queue + return; + uint32 queueSlot = _player->AddBattleGroundQueueId(bgTypeId); + if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) + { + WorldPacket data; + // fill data packet + //SendPacket(data); + return; + } + + // 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); + sBattleGroundMgr.m_BattleGroundQueues[bgTypeId].AddPlayer(_player, bgTypeId); + } +} + +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) // cheating? + return; + + // can't be received if player not in BG queue + if(!_player->InBattleGroundQueueForBattleGroundType(bgTypeId)) + 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 unk1; + uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1 + uint32 bgTypeId; // type id from dbc + uint16 unk; // 0x1F90 constant? + uint8 action; // enter battle 0x1, leave queue 0x0 + + recv_data >> unk1 >> unk2 >> bgTypeId >> unk >> action; + + if(bgTypeId >= MAX_BATTLEGROUND_TYPES) // cheating? + return; + + if(!_player->InBattleGroundQueueForBattleGroundType(bgTypeId)) + return; + + BattleGround *bg = sBattleGroundMgr.GetBattleGround(bgTypeId); + if(!bg) + return; + + uint32 queueSlot = 0; + WorldPacket data; + switch(action) + { + case 1: // port to battleground + // cheating? + if(!_player->IsInvitedForBattleGroundType(bgTypeId)) + return; + + // check if player is not deserter + if( !_player->CanJoinToBattleground() ) + { + WorldPacket data2; + data2.Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4); + data2 << (uint32) 0xFFFFFFFE; + SendPacket(&data2); + return; + } + + // if the player is dead, resurrect him before teleport + if(!_player->isAlive()) + { + _player->ResurrectPlayer(1.0f,false); + _player->SpawnCorpseBones(); + } + + // leave current group + _player->RemoveFromGroup(); + + // packet to player about BG status + queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId); + 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[bgTypeId].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); + + _player->SetBattleGroundId(bg->GetTypeID()); + sBattleGroundMgr.SendToBattleGround(_player, bgTypeId); + bg->AddPlayer(_player); + break; + case 0: // leave queue + queueSlot = _player->GetBattleGroundQueueIndex(bgTypeId); + _player->RemoveBattleGroundQueueId(bgTypeId); // 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[bgTypeId].RemovePlayer(_player->GetGUID(), true); + SendPacket(&data); + 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 queueSlot = _player->GetBattleGroundQueueIndex(bg->GetTypeID()); + 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); + if (i == queueSlot || !queue_id) + continue; + BattleGround *bg2 = sBattleGroundMgr.GetBattleGround(queue_id); + 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); + 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++) + { + if(uint32 queue_id = _player->GetBattleGroundQueueId(i)) + { + if(BattleGround *bg = sBattleGroundMgr.GetBattleGround(queue_id)) + { + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, _player->GetTeam(), i, STATUS_WAIT_QUEUE, 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; + + for(int qId = 0; qId < PLAYER_MAX_BATTLEGROUND_QUEUES; ++qId) + { + if(_player->GetBattleGroundQueueId(qId) != 0) + return; + } + + uint64 guid; // arena Battlemaster guid + uint8 type; // 2v2, 3v3 or 5v5 + uint8 asGroup; // asGroup + uint8 isRated; // isRated + 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; + + 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; + } + + if(isRated && !_player->GetArenaTeamId(type)) // player not in arena team of that size + { + _player->GetSession()->SendNotInArenaTeamPacket(arenatype); + return; + } + + if(asGroup && !_player->GetGroup()) // player not in group + return; + + // check existence + BattleGround *bg = sBattleGroundMgr.GetBattleGround(BATTLEGROUND_AA); + if(!bg) + return; + + bg->SetArenaType(arenatype); + bg->SetRated(isRated); + + if(asGroup && _player->GetGroup()) + { + Group *grp = _player->GetGroup(); + for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + if(!member) continue; + + /*if (!member->CanJoinToBattleground()) + //player has deserter aura .. do nothing + */ + + if (member->InBattleGroundQueueForBattleGroundType(BATTLEGROUND_AA)) + //player is already in this queue + continue; + + // add to queue + uint32 queueSlot = member->AddBattleGroundQueueId(BATTLEGROUND_AA); + if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) + { + WorldPacket data; + //fill data + //member->GetSession()->SendPacket(data); + continue; + } + + // 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, BATTLEGROUND_AA); + member->GetSession()->SendPacket(&data); + sBattleGroundMgr.m_BattleGroundQueues[BATTLEGROUND_AA].AddPlayer(member, BATTLEGROUND_AA); + } + } + else + { + /*if (!member->CanJoinToBattleground()) + //player has deserter aura .. do nothing + */ + + if (_player->InBattleGroundQueueForBattleGroundType(BATTLEGROUND_AA)) + //player is already in this queue + return; + + uint32 queueSlot = _player->AddBattleGroundQueueId(BATTLEGROUND_AA); + if (queueSlot == PLAYER_MAX_BATTLEGROUND_QUEUES) + { + WorldPacket data; + //fill data (player is in 3 queues already) + //SendPacket(data); + return; + } + + // 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); + sBattleGroundMgr.m_BattleGroundQueues[BATTLEGROUND_AA].AddPlayer(_player, BATTLEGROUND_AA); + } +} + +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/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp new file mode 100644 index 00000000000..52dad08345b --- /dev/null +++ b/src/game/BattleGroundMgr.cpp @@ -0,0 +1,867 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Player.h" +#include "BattleGroundMgr.h" +#include "BattleGroundAV.h" +#include "BattleGroundAB.h" +#include "BattleGroundEY.h" +#include "BattleGroundWS.h" +#include "BattleGroundNA.h" +#include "BattleGroundBE.h" +#include "BattleGroundAA.h" +#include "BattleGroundRL.h" +#include "SharedDefines.h" +#include "Policies/SingletonImp.h" +#include "MapManager.h" +#include "ObjectMgr.h" +#include "ProgressBar.h" +#include "World.h" +#include "Chat.h" + +INSTANTIATE_SINGLETON_1( BattleGroundMgr ); + +/*********************************************************/ +/*** BATTLEGROUND QUEUE SYSTEM ***/ +/*********************************************************/ + +BattleGroundQueue::BattleGroundQueue() +{ + //queues are empty, we don't have to call clear() + for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) + { + m_QueuedPlayers[i].Horde = 0; + m_QueuedPlayers[i].Alliance = 0; + //m_QueuedPlayers[i].AverageTime = 0; + } +} + +BattleGroundQueue::~BattleGroundQueue() +{ + for (int i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) + { + m_QueuedPlayers[i].clear(); + } +} + +void BattleGroundQueue::AddPlayer(Player *plr, uint32 bgTypeId) +{ + uint32 queue_id = plr->GetBattleGroundQueueIdFromLevel(); + + //if player isn't in queue, he is added, if already is, then values are overwritten, no memory leak + PlayerQueueInfo& info = m_QueuedPlayers[queue_id][plr->GetGUID()]; + info.InviteTime = 0; + info.IsInvitedToBGInstanceGUID = 0; + info.LastInviteTime = 0; + info.LastOnlineTime = getMSTime(); + info.Team = plr->GetTeam(); + + //add player to waiting order queue + m_PlayersSortedByWaitTime[queue_id].push_back(plr->GetGUID()); + + if(plr->GetTeam() == ALLIANCE) + ++m_QueuedPlayers[queue_id].Alliance; + else + ++m_QueuedPlayers[queue_id].Horde; + + this->Update(bgTypeId, queue_id); + + if( sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE) ) + { + BattleGround* bg = sBattleGroundMgr.GetBattleGround(bgTypeId); + char const* bgName = bg->GetName(); + + uint32 q_min_level = Player::GetMinLevelForBattleGroundQueueId(queue_id); + uint32 q_max_level = Player::GetMaxLevelForBattleGroundQueueId(queue_id); + + // replace hardcoded max level by player max level for nice output + if(q_max_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + q_max_level = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); + + int8 MinPlayers = bg->GetMinPlayersPerTeam(); + + uint8 qHorde = m_QueuedPlayers[queue_id].Horde; + uint8 qAlliance = m_QueuedPlayers[queue_id].Alliance; + + // Show queue status to player only (when joining queue) + if(sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY)) + { + ChatHandler(plr).PSendSysMessage(LANG_BG_QUEUE_ANNOUNCE_SELF, + bgName, q_min_level, q_max_level, qAlliance, MinPlayers - qAlliance, qHorde, MinPlayers - qHorde); + } + // System message + else + { + sWorld.SendWorldText(LANG_BG_QUEUE_ANNOUNCE_WORLD, + bgName, q_min_level, q_max_level, qAlliance, MinPlayers - qAlliance, qHorde, MinPlayers - qHorde); + } + } +} + +void BattleGroundQueue::RemovePlayer(uint64 guid, bool decreaseInvitedCount) +{ + Player *plr = objmgr.GetPlayer(guid); + + uint32 queue_id = 0; + QueuedPlayersMap::iterator itr; + bool IsSet = false; + if(!plr) + { //player is offline, we need to find him somewhere in queues + /// there is something wrong if this code is run, because we have in queue only online players! + sLog.outError("Battleground: removing offline player from BG queue - this might not happen, but it should not cause crash"); + for (uint32 i = 0; i < MAX_BATTLEGROUND_QUEUES; i++) + { + itr = m_QueuedPlayers[i].find(guid); + if(itr != m_QueuedPlayers[i].end()) + { + queue_id = i; + IsSet = true; + break; + } + } + } + else + { //player is online, we have his level, so we can find exact queue from his level + queue_id = plr->GetBattleGroundQueueIdFromLevel(); + itr = m_QueuedPlayers[queue_id].find(guid); + IsSet = true; + } + + //all variables are set, so remove player + //remove player from time queue + m_PlayersSortedByWaitTime[queue_id].remove(guid); + + if (IsSet && itr != m_QueuedPlayers[queue_id].end()) + { + if (!itr->second.IsInvitedToBGInstanceGUID) + { + if(itr->second.Team == ALLIANCE) + --m_QueuedPlayers[queue_id].Alliance; + else + --m_QueuedPlayers[queue_id].Horde; + } + else + { + if (decreaseInvitedCount) + { + BattleGround* bg = sBattleGroundMgr.GetBattleGround(itr->second.IsInvitedToBGInstanceGUID); + if (bg) + bg->DecreaseInvitedCount(itr->second.Team); + } + } + m_QueuedPlayers[queue_id].erase(itr); + } +} + +/* +this method is called when player is inserted, or removed from BG Queue - there is only one player's status changed, so we don't use while(true) cycles to invite whole queue +add method calls this by itself, the remove method could works in other way, so you have to call this method from other code after calling remove method +*/ +void BattleGroundQueue::Update(uint32 bgTypeId, uint32 queue_id) +{ + if (queue_id >= MAX_BATTLEGROUND_QUEUES) + { + //this is error, that caused crashes (not in , but now it shouldn't) + sLog.outError("BattleGroundQueue::Update() called for non existing queue type - this can cause crash, pls report problem, if this is the last line of error log before crash"); + return; + } + + //if no players in queue ... do nothing + if (this->m_QueuedPlayers[queue_id].Alliance == 0 && this->m_QueuedPlayers[queue_id].Horde == 0) + return; + + //battleground with free slot for player should be always the last in this queue + for (BGFreeSlotQueueType::iterator itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); ++itr) + { + // battleground is running, so if: + // DO NOT allow queue manager to invite new player to running arena + if ((*itr)->isBattleGround() && (*itr)->GetQueueType() == queue_id && (*itr)->GetStatus() > STATUS_WAIT_QUEUE && (*itr)->GetStatus() < STATUS_WAIT_LEAVE) + { + //we must check both teams + BattleGround* bg = *itr; //we have to store battleground pointer here, because when battleground is full, it is removed from free queue (not yet implemented!!) + // and iterator is invalid + + //check if there are some players in queue + if (m_QueuedPlayers[queue_id].Alliance > 0 || m_QueuedPlayers[queue_id].Horde > 0) + { + for (PlayerGuidsSortedByTimeQueue::iterator itr2 = m_PlayersSortedByWaitTime[queue_id].begin(); itr2 != m_PlayersSortedByWaitTime[queue_id].end();) + { + Player* plr = objmgr.GetPlayer(*itr2); + if (!plr) + { + //something is wrong!, kick player from queue + sLog.outError("BATTLEGROUND: problem with inviting offline player to Battleground queue .... pls report bug"); + uint64 oldval = *itr2; + itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); + RemovePlayer(oldval, true); + continue; + } + + // player will be invited, if in bg there is a free slot for him + if (bg->HasFreeSlotsForTeam(plr->GetTeam())) + { + // iterator to player's queue status + QueuedPlayersMap::iterator itrPlayerStatus = m_QueuedPlayers[queue_id].find(*itr2); + + // remove him from time queue + itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); + + // only check to be sure ... but this condition shouldn't be true (if it is true, then there is a bug somewhere and pls report it) + if (itrPlayerStatus == m_QueuedPlayers[queue_id].end()) + continue; + + // check if player is not already invited + if (!itrPlayerStatus->second.IsInvitedToBGInstanceGUID) + { + itrPlayerStatus->second.IsInvitedToBGInstanceGUID = bg->GetInstanceID(); + itrPlayerStatus->second.InviteTime = getMSTime(); + itrPlayerStatus->second.LastInviteTime = getMSTime(); + if(itrPlayerStatus->second.Team == ALLIANCE) + --m_QueuedPlayers[queue_id].Alliance; + else + --m_QueuedPlayers[queue_id].Horde; + sBattleGroundMgr.InvitePlayer(plr, bg->GetInstanceID()); + + WorldPacket data; + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgTypeId); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0); + plr->GetSession()->SendPacket(&data); + } + } + else + ++itr2; + + //if battleground is FULL, then it is removed from free slot queue - not yet implemented! + if (!bg->HasFreeSlots()) + { + //if bg is full, there is no need to invite other players, so break + break; + //remove BG from BGFreeSlotQueue - not used now, in this system we don't remove BGs from free queue + //bg->RemoveFromBGFreeSlotQueue() --- do not uncomment this - not yet implemented + } + } + } + } + } + + /* THIS IS A CASE THAT IN QUEUE THERE IS ENOUGHT PLAYERS TO START NEW BG */ + //itr->end is the last BG - template, which is not already started! + + /* here will be a most of change, when we create battlegrounds instantiated */ + /* if (there is enough players to start new BG) + Battleground* newbg = sBattleGroundMgr.CreateNewBattleGround(bgTypeId) + - that function will use the COPY constructor on BattleGround class ( in bg manager we should have one battleground as a template + (battleground template will be used only to create new BGs, it will be an instance of BG class, but it won't ever start) */ + + /* following code is working with current Battleground system and it should be removed, when BGs will work like instances */ + BattleGround* bg2 = sBattleGroundMgr.GetBattleGround(bgTypeId); + if (bg2->GetQueueType() != MAX_BATTLEGROUND_QUEUES || bg2->GetStatus() != STATUS_WAIT_QUEUE) + return; + if (m_QueuedPlayers[queue_id].Alliance >= bg2->GetMinPlayersPerTeam() && m_QueuedPlayers[queue_id].Horde >= bg2->GetMinPlayersPerTeam()) + { + bg2->SetStatus(STATUS_WAIT_JOIN); + bg2->SetQueueType(queue_id); + + for (PlayerGuidsSortedByTimeQueue::iterator itr2 = m_PlayersSortedByWaitTime[queue_id].begin(); itr2 != m_PlayersSortedByWaitTime[queue_id].end();) + { + Player* plr = objmgr.GetPlayer(*itr2); + if (!plr) + { + //something is wrong!, kick player from queue + sLog.outError("BATTLEGROUND: problem with inviting offline player to Battleground queue .... pls report bug"); + uint64 oldval = *itr2; + itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); + RemovePlayer(oldval, true); + continue; + } + + /* TODO: (i'm not sure this code will be useful: + here should be some condition like if (bg2->isArena() && bg2->isRated()) + { + invite players from 1 certain group on each faction to play arena match + } else if ....and existing code + */ + // player will be invited, if in bg there is a free slot for him + if (bg2->HasFreeSlotsForTeam(plr->GetTeam())) + { + // iterator to player's queue status + QueuedPlayersMap::iterator itrPlayerStatus = m_QueuedPlayers[queue_id].find(*itr2); + + // remove him from time queue + itr2 = m_PlayersSortedByWaitTime[queue_id].erase(itr2); + + // only check to be sure ... but this condition shouldn't be true (if it is true, then there is a bug somewhere and report it) + if (itrPlayerStatus == m_QueuedPlayers[queue_id].end()) + continue; + + //check if player is not already invited + if (!itrPlayerStatus->second.IsInvitedToBGInstanceGUID) + { + itrPlayerStatus->second.IsInvitedToBGInstanceGUID = bg2->GetInstanceID(); + itrPlayerStatus->second.InviteTime = getMSTime(); + itrPlayerStatus->second.LastInviteTime = getMSTime(); + + if(itrPlayerStatus->second.Team == ALLIANCE) + --m_QueuedPlayers[queue_id].Alliance; + else + --m_QueuedPlayers[queue_id].Horde; + + sBattleGroundMgr.InvitePlayer(plr, bg2->GetInstanceID()); + + WorldPacket data; + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgTypeId); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg2, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0); + plr->GetSession()->SendPacket(&data); + } + } + else + ++itr2; + } + bg2->StartBattleGround(); + } +} + +/*********************************************************/ +/*** BATTLEGROUND QUEUE EVENTS ***/ +/*********************************************************/ + +bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 p_time) +{ + Player* plr = objmgr.GetPlayer( m_PlayerGuid ); + + // player logged off (we should do nothing, he is correctly removed from queue in another procedure) + if (!plr) + return true; + + // Player can be in another BG queue and must be removed in normal way in any case + // // player is already in battleground ... do nothing (battleground queue status is deleted when player is teleported to BG) + // if (plr->GetBattleGroundId() > 0) + // return true; + + BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); + if (!bg) + return true; + + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue + { + // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems + BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; + BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); + if (qItr != qpMap.end() && qItr->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + { + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME/2, 0); + plr->GetSession()->SendPacket(&data); + } + } + return true; //event will be deleted +} + +void BGQueueInviteEvent::Abort(uint64 /*e_time*/) +{ + //this should not be called + sLog.outError("Battleground invite event ABORTED!"); +} + +bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) +{ + Player* plr = objmgr.GetPlayer( m_PlayerGuid ); + if (!plr) + // player logged off (we should do nothing, he is correctly removed from queue in another procedure) + return true; + + // Player can be in another BG queue and must be removed in normal way in any case + //if (plr->InBattleGround()) + // // player is already in battleground ... do nothing (battleground queue status is deleted when player is teleported to BG) + // return true; + + BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID); + if (!bg) + return true; + + uint32 queueSlot = plr->GetBattleGroundQueueIndex(bg->GetTypeID()); + if (queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES) // player is in queue (base at player data + { + // check if player is invited to this bg ... this check must be here, because when player leaves queue and joins another, it would cause a problems + BattleGroundQueue::QueuedPlayersMap const& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].m_QueuedPlayers[plr->GetBattleGroundQueueIdFromLevel()]; + BattleGroundQueue::QueuedPlayersMap::const_iterator qItr = qpMap.find(m_PlayerGuid); + if (qItr!=qpMap.end() && qItr->second.IsInvitedToBGInstanceGUID == m_BgInstanceGUID) + { + plr->RemoveBattleGroundQueueId(bg->GetTypeID()); + sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].RemovePlayer(m_PlayerGuid, true); + sBattleGroundMgr.m_BattleGroundQueues[bg->GetTypeID()].Update(bg->GetTypeID(), bg->GetQueueType()); + + WorldPacket data; + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, plr->GetTeam(), queueSlot, STATUS_NONE, 0, 0); + plr->GetSession()->SendPacket(&data); + } + } + + //event will be deleted + return true; +} + +void BGQueueRemoveEvent::Abort(uint64 /*e_time*/) +{ + //this should not be called + sLog.outError("Battleground remove event ABORTED!"); +} + +/*********************************************************/ +/*** BATTLEGROUND MANAGER ***/ +/*********************************************************/ + +BattleGroundMgr::BattleGroundMgr() +{ + m_BattleGrounds.clear(); +} + +BattleGroundMgr::~BattleGroundMgr() +{ + for(std::map::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr) + delete itr->second; + m_BattleGrounds.clear(); +} + +void BattleGroundMgr::Update(time_t diff) +{ + for(BattleGroundSet::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr) + itr->second->Update(diff); +} + +void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2) +{ + // we can be in 3 queues in same time... + if(StatusID == 0) + { + data->Initialize(SMSG_BATTLEFIELD_STATUS, 4*3); + *data << uint32(QueueSlot); // queue id (0...2) + *data << uint64(0); + return; + } + + data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+1+1+4+2+4+1+4+4+4)); + *data << uint32(QueueSlot); // queue id (0...2) - player can be in 3 queues in time + // uint64 in client + *data << uint64( uint64(bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); + *data << uint32(0); // unknown + // alliance/horde for BG and skirmish/rated for Arenas + *data << uint8(bg->isArena() ? (bg->isRated() ? 1 : 0) : bg->GetTeamIndexByTeamId(team)); + *data << uint32(StatusID); // status + switch(StatusID) + { + case STATUS_WAIT_QUEUE: // status_in_queue + *data << uint32(Time1); // average wait time, milliseconds + *data << uint32(Time2); // time in queue, updated every minute? + break; + case STATUS_WAIT_JOIN: // status_invite + *data << uint32(bg->GetMapId()); // map id + *data << uint32(Time1); // time to remove from queue, milliseconds + break; + case STATUS_IN_PROGRESS: // status_in_progress + *data << uint32(bg->GetMapId()); // map id + *data << uint32(Time1); // 0 at bg start, 120000 after bg end, time to bg auto leave, milliseconds + *data << uint32(Time2); // time from bg start, milliseconds + *data << uint8(0x1); // unk sometimes 0x0! + break; + default: + sLog.outError("Unknown BG status!"); + break; + } +} + +void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg) +{ + uint8 type = (bg->isArena() ? 1 : 0); + // last check on 2.4.1 + data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize())); + *data << uint8(type); // seems to be type (battleground=0/arena=1) + if(type) // arena + { + for(uint8 i = 0; i < 2; i++) + { + *data << uint32(3000+1+i); // rating change: showed value - 3000 + *data << uint32(0); // 2.4.0, has some to do with rating change... + *data << uint8(0); // some unknown string + } + } + + if(bg->GetWinner() == 2) + { + *data << uint8(0); // bg in progress + } + else + { + *data << uint8(1); // bg ended + *data << uint8(bg->GetWinner()); // who win + } + + *data << (int32)(bg->GetPlayerScoresSize()); + + for(std::map::const_iterator itr = bg->GetPlayerScoresBegin(); itr != bg->GetPlayerScoresEnd(); ++itr) + { + *data << (uint64)itr->first; + *data << (int32)itr->second->KillingBlows; + if(type) + { + // this value is team (green/gold)? + // that part probably wrong + Player *plr = objmgr.GetPlayer(itr->first); + if(plr) + { + if(plr->GetTeam() == HORDE) + *data << uint8(0); + else if(plr->GetTeam() == ALLIANCE) + *data << uint8(1); + else + *data << uint8(0); + } + else + *data << uint8(0); + } + else + { + *data << (int32)itr->second->HonorableKills; + *data << (int32)itr->second->Deaths; + *data << (int32)itr->second->BonusHonor; // bonus honor + } + *data << (int32)itr->second->DamageDone; // damage done + *data << (int32)itr->second->HealingDone; // healing done + switch(bg->GetTypeID()) // battleground specific things + { + case BATTLEGROUND_AV: + *data << (uint32)0x00000005; // count of next fields + *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsAssaulted; // GraveyardsAssaulted + *data << (uint32)((BattleGroundAVScore*)itr->second)->GraveyardsDefended; // GraveyardsDefended + *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersAssaulted; // TowersAssaulted + *data << (uint32)((BattleGroundAVScore*)itr->second)->TowersDefended; // TowersDefended + *data << (uint32)((BattleGroundAVScore*)itr->second)->MinesCaptured; // MinesCaptured + break; + case BATTLEGROUND_WS: + *data << (uint32)0x00000002; // count of next fields + *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagCaptures; // flag captures + *data << (uint32)((BattleGroundWGScore*)itr->second)->FlagReturns; // flag returns + break; + case BATTLEGROUND_AB: + *data << (uint32)0x00000002; // count of next fields + *data << (uint32)((BattleGroundABScore*)itr->second)->BasesAssaulted; // bases asssulted + *data << (uint32)((BattleGroundABScore*)itr->second)->BasesDefended; // bases defended + break; + case BATTLEGROUND_EY: + *data << (uint32)0x00000001; // count of next fields + *data << (uint32)((BattleGroundEYScore*)itr->second)->FlagCaptures; // flag captures + break; + case BATTLEGROUND_NA: + case BATTLEGROUND_BE: + case BATTLEGROUND_AA: + case BATTLEGROUND_RL: + *data << (int32)0; // 0 + break; + default: + sLog.outDebug("Unhandled MSG_PVP_LOG_DATA for BG id %u", bg->GetTypeID()); + *data << (int32)0; + break; + } + } +} + +void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, uint32 bgTypeId) +{ + /*bgTypeId is: + 0 - Your group has joined a battleground queue, but you are not eligible + 1 - Your group has joined the queue for AV + 2 - Your group has joined the queue for WS + 3 - Your group has joined the queue for AB + 4 - Your group has joined the queue for NA + 5 - Your group has joined the queue for BE Arena + 6 - Your group has joined the queue for All Arenas + 7 - Your group has joined the queue for EotS*/ + data->Initialize(SMSG_GROUP_JOINED_BATTLEGROUND, 4); + *data << uint32(bgTypeId); +} + +void BattleGroundMgr::BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value) +{ + data->Initialize(SMSG_UPDATE_WORLD_STATE, 4+4); + *data << uint32(field); + *data << uint32(value); +} + +void BattleGroundMgr::BuildPlaySoundPacket(WorldPacket *data, uint32 soundid) +{ + data->Initialize(SMSG_PLAY_SOUND, 4); + *data << uint32(soundid); +} + +void BattleGroundMgr::BuildPlayerLeftBattleGroundPacket(WorldPacket *data, Player *plr) +{ + data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8); + *data << uint64(plr->GetGUID()); +} + +void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr) +{ + data->Initialize(SMSG_BATTLEGROUND_PLAYER_JOINED, 8); + *data << uint64(plr->GetGUID()); +} + +void BattleGroundMgr::InvitePlayer(Player* plr, uint32 bgInstanceGUID) +{ + // set invited player counters: + BattleGround* bg = this->GetBattleGround(bgInstanceGUID); + if(!bg) + return; + + bg->IncreaseInvitedCount(plr->GetTeam()); + plr->SetInviteForBattleGroundType(bg->GetTypeID()); + // create invite events: + //add events to player's counters ---- this is not good way - there should be something like global event processor, where we should add those events + BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), bgInstanceGUID); + plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME/2)); + BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), bgInstanceGUID, plr->GetTeam()); + plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME)); +} + +uint32 BattleGroundMgr::CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO) +{ + // Create the BG + BattleGround *bg = NULL; + + switch(bgTypeId) + { + case BATTLEGROUND_AV: bg = new BattleGroundAV; break; + case BATTLEGROUND_WS: bg = new BattleGroundWS; break; + case BATTLEGROUND_AB: bg = new BattleGroundAB; break; + case BATTLEGROUND_NA: bg = new BattleGroundNA; break; + case BATTLEGROUND_BE: bg = new BattleGroundBE; break; + case BATTLEGROUND_AA: bg = new BattleGroundAA; break; + case BATTLEGROUND_EY: bg = new BattleGroundEY; break; + case BATTLEGROUND_RL: bg = new BattleGroundRL; break; + default:bg = new BattleGround; break; // placeholder for non implemented BG + } + + bg->SetMapId(MapID); + bg->Reset(); + if(!bg->SetupBattleGround()) + { + delete bg; + return 0; + } + + BattlemasterListEntry const *bl = sBattlemasterListStore.LookupEntry(bgTypeId); + //in previous method is checked if exists entry in sBattlemasterListStore, so no check needed + if (bl) + { + bg->SetArenaorBGType(bl->type == TYPE_ARENA); + } + + bg->SetTypeID(bgTypeId); + bg->SetInstanceID(bgTypeId); // temporary + bg->SetMinPlayersPerTeam(MinPlayersPerTeam); + bg->SetMaxPlayersPerTeam(MaxPlayersPerTeam); + bg->SetMinPlayers(MinPlayersPerTeam*2); + bg->SetMaxPlayers(MaxPlayersPerTeam*2); + bg->SetName(BattleGroundName); + bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO); + bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO); + bg->SetLevelRange(LevelMin, LevelMax); + //add BaggleGround instance to FreeSlotQueue + bg->AddToBGFreeSlotQueue(); + + AddBattleGround(bg->GetInstanceID(), bg); + //sLog.outDetail("BattleGroundMgr: Created new battleground: %u %s (Map %u, %u players per team, Levels %u-%u)", bg_TypeID, bg->m_Name, bg->m_MapId, bg->m_MaxPlayersPerTeam, bg->m_LevelMin, bg->m_LevelMax); + return bg->GetInstanceID(); +} + +void BattleGroundMgr::CreateInitialBattleGrounds() +{ + float AStartLoc[4]; + float HStartLoc[4]; + uint32 MaxPlayersPerTeam, MinPlayersPerTeam, MinLvl, MaxLvl, start1, start2; + BattlemasterListEntry const *bl; + WorldSafeLocsEntry const *start; + + uint32 count = 0; + + // 0 1 2 3 4 5 6 7 8 + QueryResult *result = WorldDatabase.Query("SELECT id, MinPlayersPerTeam,MaxPlayersPerTeam,MinLvl,MaxLvl,AllianceStartLoc,AllianceStartO,HordeStartLoc,HordeStartO FROM battleground_template"); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 battlegrounds. DB table `battleground_template` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 bgTypeID = fields[0].GetUInt32(); + + // can be overwrited by values from DB + bl = sBattlemasterListStore.LookupEntry(bgTypeID); + if(!bl) + { + sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.",bgTypeID); + continue; + } + + MaxPlayersPerTeam = bl->maxplayersperteam; + MinPlayersPerTeam = bl->maxplayersperteam/2; + MinLvl = bl->minlvl; + MaxLvl = bl->maxlvl; + + if(fields[1].GetUInt32()) + MinPlayersPerTeam = fields[1].GetUInt32(); + + if(fields[2].GetUInt32()) + MaxPlayersPerTeam = fields[2].GetUInt32(); + + if(fields[3].GetUInt32()) + MinLvl = fields[3].GetUInt32(); + + if(fields[4].GetUInt32()) + MaxLvl = fields[4].GetUInt32(); + + start1 = fields[5].GetUInt32(); + + start = sWorldSafeLocsStore.LookupEntry(start1); + if(start) + { + AStartLoc[0] = start->x; + AStartLoc[1] = start->y; + AStartLoc[2] = start->z; + AStartLoc[3] = fields[6].GetFloat(); + } + else if(bgTypeID == BATTLEGROUND_AA) + { + AStartLoc[0] = 0; + AStartLoc[1] = 0; + AStartLoc[2] = 0; + AStartLoc[3] = fields[6].GetFloat(); + } + else + { + sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.",bgTypeID,start1); + continue; + } + + start2 = fields[7].GetUInt32(); + + start = sWorldSafeLocsStore.LookupEntry(start2); + if(start) + { + HStartLoc[0] = start->x; + HStartLoc[1] = start->y; + HStartLoc[2] = start->z; + HStartLoc[3] = fields[8].GetFloat(); + } + else if(bgTypeID == BATTLEGROUND_AA) + { + HStartLoc[0] = 0; + HStartLoc[1] = 0; + HStartLoc[2] = 0; + HStartLoc[3] = fields[8].GetFloat(); + } + else + { + sLog.outErrorDb("Table `battleground_template` for id %u have non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.",bgTypeID,start2); + continue; + } + + //sLog.outDetail("Creating battleground %s, %u-%u", bl->name[sWorld.GetDBClang()], MinLvl, MaxLvl); + if(!CreateBattleGround(bgTypeID, MinPlayersPerTeam, MaxPlayersPerTeam, MinLvl, MaxLvl, bl->name[sWorld.GetDefaultDbcLocale()], bl->mapid[0], AStartLoc[0], AStartLoc[1], AStartLoc[2], AStartLoc[3], HStartLoc[0], HStartLoc[1], HStartLoc[2], HStartLoc[3])) + continue; + + ++count; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u battlegrounds", count ); +} + +void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, uint64 guid, Player* plr, uint32 bgTypeId) +{ + uint32 PlayerLevel = 10; + + if(plr) + PlayerLevel = plr->getLevel(); + + data->Initialize(SMSG_BATTLEFIELD_LIST); + *data << uint64(guid); // battlemaster guid + *data << uint32(bgTypeId); // battleground id + if(bgTypeId == BATTLEGROUND_AA) // arena + { + *data << uint8(5); // unk + *data << uint32(0); // unk + } + else // battleground + { + *data << uint8(0x00); // unk + + size_t count_pos = data->wpos(); + uint32 count = 0; + *data << uint32(0x00); // number of bg instances + + for(std::map::iterator itr = m_BattleGrounds.begin(); itr != m_BattleGrounds.end(); ++itr) + { + if(itr->second->GetTypeID() == bgTypeId && (PlayerLevel >= itr->second->GetMinLevel()) && (PlayerLevel <= itr->second->GetMaxLevel())) + { + *data << uint32(itr->second->GetInstanceID()); + ++count; + } + } + data->put( count_pos , count); + } +} + +void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 bgTypeId) +{ + BattleGround *bg = GetBattleGround(bgTypeId); + if(bg) + { + uint32 mapid = bg->GetMapId(); + float x, y, z, O; + bg->GetTeamStartLoc(pl->GetTeam(), x, y, z, O); + + sLog.outDetail("BATTLEGROUND: Sending %s to map %u, X %f, Y %f, Z %f, O %f", pl->GetName(), mapid, x, y, z, O); + pl->TeleportTo(mapid, x, y, z, O); + } +} + +void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid) +{ + WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12); + uint32 time_ = 30000 - bg->GetLastResurrectTime(); // resurrect every 30 seconds + if(time_ == uint32(-1)) + time_ = 0; + data << guid << time_; + pl->GetSession()->SendPacket(&data); +} diff --git a/src/game/BattleGroundMgr.h b/src/game/BattleGroundMgr.h new file mode 100644 index 00000000000..90eb4d2f775 --- /dev/null +++ b/src/game/BattleGroundMgr.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDMGR_H +#define __BATTLEGROUNDMGR_H + +#include "BattleGround.h" +#include "Policies/Singleton.h" + +class BattleGround; + +//TODO it is not possible to have this structure, because we should have BattlegroundSet for each queue +//so i propose to change this type to array 1..MAX_BATTLEGROUND_TYPES of sets or maps.. +typedef std::map BattleGroundSet; +//typedef std::map BattleGroundQueueSet; +typedef std::deque BGFreeSlotQueueType; + +#define MAX_BATTLEGROUND_QUEUES 7 // for level ranges 10-19, 20-29, 30-39, 40-49, 50-59, 60-69, 70+ + +#define MAX_BATTLEGROUND_TYPES 9 // each BG type will be in array + +struct PlayerQueueInfo +{ + uint32 InviteTime; // first invite time + uint32 LastInviteTime; // last invite time + uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG + uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes + uint32 Team; // Player team (ALLIANCE/HORDE) + bool IsRated; + bool AsGroup; // uint32 GroupId; + uint8 ArenaType; +}; + +struct PlayersCount +{ + uint32 Alliance; + uint32 Horde; +}; + +template class bgqueue: public std::map<_Kty, _Ty> +{ + public: + uint32 Alliance; + uint32 Horde; + //bool Ready; // not used now + //uint32 AverageTime; //not already implemented (it should be average time in queue for last 10 players) +}; + +class BattleGroundQueue +{ + public: + BattleGroundQueue(); + ~BattleGroundQueue(); +/* + uint32 GetType(); + void SetType(uint32 type);*/ + + void Update(uint32 bgTypeId, uint32 queue_id); + + void AddPlayer(Player *plr, uint32 bgTypeId); + void RemovePlayer(uint64 guid, bool decreaseInvitedCount); + + typedef bgqueue QueuedPlayersMap; + QueuedPlayersMap m_QueuedPlayers[MAX_BATTLEGROUND_QUEUES]; + typedef std::list PlayerGuidsSortedByTimeQueue; + PlayerGuidsSortedByTimeQueue m_PlayersSortedByWaitTime[MAX_BATTLEGROUND_QUEUES]; +}; + +/* + This class is used to invite player to BG again, when minute lasts from his first invitation + it is capable to solve all possibilities +*/ +class BGQueueInviteEvent : public BasicEvent +{ + public: + BGQueueInviteEvent(uint64 pl_guid, uint32 BgInstanceGUID) : m_PlayerGuid(pl_guid), m_BgInstanceGUID(BgInstanceGUID) {}; + virtual ~BGQueueInviteEvent() {}; + + virtual bool Execute(uint64 e_time, uint32 p_time); + virtual void Abort(uint64 e_time); + private: + uint64 m_PlayerGuid; + uint32 m_BgInstanceGUID; + +}; + +/* + This class is used to remove player from BG queue after 2 minutes from first invitation +*/ +class BGQueueRemoveEvent : public BasicEvent +{ + public: + BGQueueRemoveEvent(uint64 pl_guid, uint32 bgInstanceGUID, uint32 playersTeam) : m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_PlayersTeam(playersTeam) {}; + virtual ~BGQueueRemoveEvent() {}; + + virtual bool Execute(uint64 e_time, uint32 p_time); + virtual void Abort(uint64 e_time); + private: + uint64 m_PlayerGuid; + uint32 m_BgInstanceGUID; + uint32 m_PlayersTeam; +}; + + +class BattleGroundMgr +{ + public: + /* Construction */ + BattleGroundMgr(); + ~BattleGroundMgr(); + void Update(time_t diff); + + /* Packet Building */ + void BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr); + void BuildPlayerLeftBattleGroundPacket(WorldPacket *data, Player *plr); + void BuildBattleGroundListPacket(WorldPacket *data, uint64 guid, Player *plr, uint32 bgTypeId); + void BuildGroupJoinedBattlegroundPacket(WorldPacket *data, uint32 bgTypeId); + void BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value); + void BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg); + void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint32 team, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2); + void BuildPlaySoundPacket(WorldPacket *data, uint32 soundid); + + /* Player invitation */ + // called from Queue update, or from Addplayer to queue + void InvitePlayer(Player* plr, uint32 bgInstanceGUID); + + /* Battlegrounds */ + BattleGroundSet::iterator GetBattleGroundsBegin() { return m_BattleGrounds.begin(); }; + BattleGroundSet::iterator GetBattleGroundsEnd() { return m_BattleGrounds.end(); }; + + BattleGround* GetBattleGround(uint8 ID) + { + BattleGroundSet::iterator i = m_BattleGrounds.find(ID); + if(i != m_BattleGrounds.end()) + return i->second; + else + return NULL; + }; + + uint32 CreateBattleGround(uint32 bgTypeId, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam, uint32 LevelMin, uint32 LevelMax, char* BattleGroundName, uint32 MapID, float Team1StartLocX, float Team1StartLocY, float Team1StartLocZ, float Team1StartLocO, float Team2StartLocX, float Team2StartLocY, float Team2StartLocZ, float Team2StartLocO); + + inline void AddBattleGround(uint32 ID, BattleGround* BG) { m_BattleGrounds[ID] = BG; }; + + void CreateInitialBattleGrounds(); + + void SendToBattleGround(Player *pl, uint32 bgTypeId); + + /* Battleground queues */ + //these queues are instantiated when creating BattlegroundMrg + BattleGroundQueue m_BattleGroundQueues[MAX_BATTLEGROUND_TYPES]; // public, because we need to access them in BG handler code + + BGFreeSlotQueueType BGFreeSlotQueue[MAX_BATTLEGROUND_TYPES]; + + void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, uint64 guid); + + private: + + /* Battlegrounds */ + BattleGroundSet m_BattleGrounds; +}; + +#define sBattleGroundMgr MaNGOS::Singleton::Instance() +#endif diff --git a/src/game/BattleGroundNA.cpp b/src/game/BattleGroundNA.cpp new file mode 100644 index 00000000000..636efe7e586 --- /dev/null +++ b/src/game/BattleGroundNA.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Object.h" +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundNA.h" +#include "Creature.h" +#include "ObjectMgr.h" +#include "MapManager.h" +#include "Language.h" + +BattleGroundNA::BattleGroundNA() +{ + m_BgObjects.resize(BG_NA_OBJECT_MAX); +} + +BattleGroundNA::~BattleGroundNA() +{ + +} + +void BattleGroundNA::Update(time_t diff) +{ + BattleGround::Update(diff); + + // after bg start we get there + if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) + { + ModifyStartDelayTime(diff); + + if (!(m_Events & 0x01)) + { + m_Events |= 0x01; + for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_4; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + + SetStartDelayTime(START_DELAY1); + SendMessageToAll(LANG_ARENA_ONE_MINUTE); + } + // After 30 seconds, warning is signalled + else if (GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x04)) + { + m_Events |= 0x04; + SendMessageToAll(LANG_ARENA_THIRTY_SECONDS); + } + // After 15 seconds, warning is signalled + else if (GetStartDelayTime() <= START_DELAY3 && !(m_Events & 0x08)) + { + m_Events |= 0x08; + SendMessageToAll(LANG_ARENA_FIFTEEN_SECONDS); + } + // delay expired (1 minute) + else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10)) + { + m_Events |= 0x10; + + for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_2; i++) + DoorOpen(i); + + SendMessageToAll(LANG_ARENA_BEGUN); + SetStatus(STATUS_IN_PROGRESS); + SetStartDelayTime(0); + + for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if(Player *plr = objmgr.GetPlayer(itr->first)) + plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + } + } + + /*if(GetStatus() == STATUS_IN_PROGRESS) + { + // update something + }*/ +} + +void BattleGroundNA::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundNAScore* sc = new BattleGroundNAScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundNA::RemovePlayer(Player* /*plr*/, uint64 /*guid*/) +{ + +} + +void BattleGroundNA::HandleKillPlayer(Player *player, Player *killer) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + if(!killer) + { + sLog.outError("BattleGroundNA: Killer player not found"); + return; + } + + BattleGround::HandleKillPlayer(player, killer); + + uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + + ++m_TeamKills[killer_team_index]; // add kills to killer's team + + if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + { + // all opponents killed + EndBattleGround(killer->GetTeam()); + } +} + +void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + //uint32 SpellId = 0; + //uint64 buff_guid = 0; + switch(Trigger) + { + case 4536: // buff trigger? + case 4537: // buff trigger? + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } + + //if(buff_guid) + // HandleTriggerBuff(buff_guid,Source); +} + +void BattleGroundNA::ResetBGSubclass() +{ + m_TeamKills[BG_TEAM_ALLIANCE] = 0; + m_TeamKills[BG_TEAM_HORDE] = 0; +} + +bool BattleGroundNA::SetupBattleGround() +{ + // gates + if( !AddObject(BG_NA_OBJECT_DOOR_1, BG_NA_OBJECT_TYPE_DOOR_1, 4031.854f, 2966.833f, 12.6462f, -2.648788f, 0, 0, 0.9697962f, -0.2439165f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_NA_OBJECT_DOOR_2, BG_NA_OBJECT_TYPE_DOOR_2, 4081.179f, 2874.97f, 12.39171f, 0.4928045f, 0, 0, 0.2439165f, 0.9697962f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_NA_OBJECT_DOOR_3, BG_NA_OBJECT_TYPE_DOOR_3, 4023.709f, 2981.777f, 10.70117f, -2.648788f, 0, 0, 0.9697962f, -0.2439165f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_NA_OBJECT_DOOR_4, BG_NA_OBJECT_TYPE_DOOR_4, 4090.064f, 2858.438f, 10.23631f, 0.4928045f, 0, 0, 0.2439165f, 0.9697962f, RESPAWN_IMMEDIATELY)) + { + sLog.outErrorDb("BatteGroundNA: Failed to spawn some object!"); + return false; + } + + return true; +} + +/* +20:12:14 id:036668 [S2C] SMSG_INIT_WORLD_STATES (706 = 0x02C2) len: 86 +0000: 2f 02 00 00 72 0e 00 00 00 00 00 00 09 00 11 0a | /...r........... +0010: 00 00 01 00 00 00 0f 0a 00 00 00 00 00 00 10 0a | ................ +0020: 00 00 00 00 00 00 d4 08 00 00 00 00 00 00 d8 08 | ................ +0030: 00 00 00 00 00 00 d7 08 00 00 00 00 00 00 d6 08 | ................ +0040: 00 00 00 00 00 00 d5 08 00 00 00 00 00 00 d3 08 | ................ +0050: 00 00 00 00 00 00 | ...... +*/ diff --git a/src/game/BattleGroundNA.h b/src/game/BattleGroundNA.h new file mode 100644 index 00000000000..1ca04e8ba2f --- /dev/null +++ b/src/game/BattleGroundNA.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDNA_H +#define __BATTLEGROUNDNA_H + +class BattleGround; + +enum BattleGroundNAObjectTypes +{ + BG_NA_OBJECT_DOOR_1 = 0, + BG_NA_OBJECT_DOOR_2 = 1, + BG_NA_OBJECT_DOOR_3 = 2, + BG_NA_OBJECT_DOOR_4 = 3, + BG_NA_OBJECT_MAX = 4 +}; + +enum BattleGroundNAObjects +{ + BG_NA_OBJECT_TYPE_DOOR_1 = 183978, + BG_NA_OBJECT_TYPE_DOOR_2 = 183980, + BG_NA_OBJECT_TYPE_DOOR_3 = 183977, + BG_NA_OBJECT_TYPE_DOOR_4 = 183979 +}; + +class BattleGroundNAScore : public BattleGroundScore +{ + public: + BattleGroundNAScore() {}; + virtual ~BattleGroundNAScore() {}; + //TODO fix me +}; + +class BattleGroundNA : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundNA(); + ~BattleGroundNA(); + void Update(time_t diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + virtual void ResetBGSubclass(); + void HandleKillPlayer(Player* player, Player *killer); + + private: + uint32 m_TeamKills[2]; // count of kills for each team +}; +#endif diff --git a/src/game/BattleGroundRL.cpp b/src/game/BattleGroundRL.cpp new file mode 100644 index 00000000000..68ba34e06ed --- /dev/null +++ b/src/game/BattleGroundRL.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Object.h" +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundRL.h" +#include "Creature.h" +#include "ObjectMgr.h" +#include "MapManager.h" +#include "Language.h" + +BattleGroundRL::BattleGroundRL() +{ + m_BgObjects.resize(BG_RL_OBJECT_MAX); +} + +BattleGroundRL::~BattleGroundRL() +{ + +} + +void BattleGroundRL::Update(time_t diff) +{ + BattleGround::Update(diff); + + if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) + { + ModifyStartDelayTime(diff); + + if (!(m_Events & 0x01)) + { + m_Events |= 0x01; + + for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + + SetStartDelayTime(START_DELAY1); + SendMessageToAll(LANG_ARENA_ONE_MINUTE); + } + // After 30 seconds, warning is signalled + else if (GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x04)) + { + m_Events |= 0x04; + SendMessageToAll(LANG_ARENA_THIRTY_SECONDS); + } + // After 15 seconds, warning is signalled + else if (GetStartDelayTime() <= START_DELAY3 && !(m_Events & 0x08)) + { + m_Events |= 0x08; + SendMessageToAll(LANG_ARENA_FIFTEEN_SECONDS); + } + // delay expired (1 minute) + else if (GetStartDelayTime() <= 0 && !(m_Events & 0x10)) + { + m_Events |= 0x10; + + for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; i++) + DoorOpen(i); + + SendMessageToAll(LANG_ARENA_BEGUN); + SetStatus(STATUS_IN_PROGRESS); + SetStartDelayTime(0); + + for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if(Player *plr = objmgr.GetPlayer(itr->first)) + plr->RemoveAurasDueToSpell(SPELL_ARENA_PREPARATION); + } + } + + /*if(GetStatus() == STATUS_IN_PROGRESS) + { + // update something + }*/ +} + +void BattleGroundRL::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundRLScore* sc = new BattleGroundRLScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundRL::RemovePlayer(Player *plr, uint64 guid) +{ + +} + +void BattleGroundRL::HandleKillPlayer(Player *player, Player *killer) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + if(!killer) + { + sLog.outError("Killer player not found"); + return; + } + + BattleGround::HandleKillPlayer(player, killer); + + uint32 killer_team_index = GetTeamIndexByTeamId(killer->GetTeam()); + + ++m_TeamKills[killer_team_index]; // add kills to killer's team + + if(m_TeamKills[killer_team_index] >= GetPlayersCountByTeam(player->GetTeam())) + { + // all opponents killed + EndBattleGround(killer->GetTeam()); + } +} + +void BattleGroundRL::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + //uint32 SpellId = 0; + //uint64 buff_guid = 0; + switch(Trigger) + { + case 4696: // buff trigger? + case 4697: // buff trigger? + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } + + //if(buff_guid) + // HandleTriggerBuff(buff_guid,Source); +} + +void BattleGroundRL::ResetBGSubclass() +{ + m_TeamKills[BG_TEAM_ALLIANCE] = 0; + m_TeamKills[BG_TEAM_HORDE] = 0; +} + +bool BattleGroundRL::SetupBattleGround() +{ + // gates + if( !AddObject(BG_RL_OBJECT_DOOR_1, BG_RL_OBJECT_TYPE_DOOR_1, 1293.561f, 1601.938f, 31.60557f, -1.457349f, 0, 0, -0.6658813f, 0.7460576f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_RL_OBJECT_DOOR_2, BG_RL_OBJECT_TYPE_DOOR_2, 1278.648f, 1730.557f, 31.60557f, 1.684245f, 0, 0, 0.7460582f, 0.6658807f, RESPAWN_IMMEDIATELY)) + { + sLog.outErrorDb("BatteGroundRL: Failed to spawn some object!"); + return false; + } + + return true; +} + +/* +Packet S->C, id 600, SMSG_INIT_WORLD_STATES (706), len 86 +0000: 3C 02 00 00 80 0F 00 00 00 00 00 00 09 00 BA 0B | <............... +0010: 00 00 01 00 00 00 B9 0B 00 00 02 00 00 00 B8 0B | ................ +0020: 00 00 00 00 00 00 D8 08 00 00 00 00 00 00 D7 08 | ................ +0030: 00 00 00 00 00 00 D6 08 00 00 00 00 00 00 D5 08 | ................ +0040: 00 00 00 00 00 00 D3 08 00 00 00 00 00 00 D4 08 | ................ +0050: 00 00 00 00 00 00 | ...... +*/ diff --git a/src/game/BattleGroundRL.h b/src/game/BattleGroundRL.h new file mode 100644 index 00000000000..2fdbfc4c83c --- /dev/null +++ b/src/game/BattleGroundRL.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __BATTLEGROUNDRL_H +#define __BATTLEGROUNDRL_H + +class BattleGround; + +enum BattleGroundRLObjectTypes +{ + BG_RL_OBJECT_DOOR_1 = 0, + BG_RL_OBJECT_DOOR_2 = 1, + BG_RL_OBJECT_MAX = 2 +}; + +enum BattleGroundRLObjects +{ + BG_RL_OBJECT_TYPE_DOOR_1 = 185918, + BG_RL_OBJECT_TYPE_DOOR_2 = 185917 +}; + +class BattleGroundRLScore : public BattleGroundScore +{ + public: + BattleGroundRLScore() {}; + virtual ~BattleGroundRLScore() {}; + //TODO fix me +}; + +class BattleGroundRL : public BattleGround +{ + friend class BattleGroundMgr; + + public: + BattleGroundRL(); + ~BattleGroundRL(); + void Update(time_t diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + bool SetupBattleGround(); + virtual void ResetBGSubclass(); + void HandleKillPlayer(Player* player, Player *killer); + + private: + uint32 m_TeamKills[2]; // count of kills for each team +}; +#endif diff --git a/src/game/BattleGroundWS.cpp b/src/game/BattleGroundWS.cpp new file mode 100644 index 00000000000..d907b6e3bcb --- /dev/null +++ b/src/game/BattleGroundWS.cpp @@ -0,0 +1,678 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Object.h" +#include "Player.h" +#include "BattleGround.h" +#include "BattleGroundWS.h" +#include "Creature.h" +#include "GameObject.h" +#include "Chat.h" +#include "MapManager.h" +#include "Language.h" + +BattleGroundWS::BattleGroundWS() +{ + m_BgObjects.resize(BG_WS_OBJECT_MAX); + m_BgCreatures.resize(BG_CREATURES_MAX_WS); +} + +BattleGroundWS::~BattleGroundWS() +{ +} + +void BattleGroundWS::Update(time_t diff) +{ + BattleGround::Update(diff); + + // after bg start we get there (once) + if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize()) + { + ModifyStartDelayTime(diff); + + if(!(m_Events & 0x01)) + { + m_Events |= 0x01; + + for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_H_4; i++) + { + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + DoorClose(i); + } + for(uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; i++) + SpawnBGObject(i, RESPAWN_ONE_DAY); + + SetStartDelayTime(START_DELAY0); + } + // After 1 minute, warning is signalled + else if(GetStartDelayTime() <= START_DELAY1 && !(m_Events & 0x04)) + { + m_Events |= 0x04; + SendMessageToAll(GetMangosString(LANG_BG_WS_ONE_MINUTE)); + } + // After 1,5 minute, warning is signalled + else if(GetStartDelayTime() <= START_DELAY2 && !(m_Events & 0x08)) + { + m_Events |= 0x08; + SendMessageToAll(GetMangosString(LANG_BG_WS_HALF_MINUTE)); + } + // After 2 minutes, gates OPEN ! x) + else if(GetStartDelayTime() < 0 && !(m_Events & 0x10)) + { + m_Events |= 0x10; + for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_A_4; i++) + DoorOpen(i); + for(uint32 i = BG_WS_OBJECT_DOOR_H_1; i <= BG_WS_OBJECT_DOOR_H_2; i++) + DoorOpen(i); + + SpawnBGObject(BG_WS_OBJECT_DOOR_A_5, RESPAWN_ONE_DAY); + SpawnBGObject(BG_WS_OBJECT_DOOR_A_6, RESPAWN_ONE_DAY); + SpawnBGObject(BG_WS_OBJECT_DOOR_H_3, RESPAWN_ONE_DAY); + SpawnBGObject(BG_WS_OBJECT_DOOR_H_4, RESPAWN_ONE_DAY); + + for(uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; i++) + SpawnBGObject(i, RESPAWN_IMMEDIATELY); + + SendMessageToAll(GetMangosString(LANG_BG_WS_BEGIN)); + + PlaySoundToAll(SOUND_BG_START); + SetStatus(STATUS_IN_PROGRESS); + + for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if(Player* plr = objmgr.GetPlayer(itr->first)) + plr->RemoveAurasDueToSpell(SPELL_PREPARATION); + } + } + else if(GetStatus() == STATUS_IN_PROGRESS) + { + if(m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) + { + m_FlagsTimer[BG_TEAM_ALLIANCE] -= diff; + + if(m_FlagsTimer[BG_TEAM_ALLIANCE] < 0) + { + m_FlagsTimer[BG_TEAM_ALLIANCE] = 0; + RespawnFlag(ALLIANCE, true); + } + } + if(m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) + { + m_FlagsDropTimer[BG_TEAM_ALLIANCE] -= diff; + + if(m_FlagsDropTimer[BG_TEAM_ALLIANCE] < 0) + { + m_FlagsDropTimer[BG_TEAM_ALLIANCE] = 0; + RespawnFlagAfterDrop(ALLIANCE); + } + } + if(m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_WAIT_RESPAWN) + { + m_FlagsTimer[BG_TEAM_HORDE] -= diff; + + if(m_FlagsTimer[BG_TEAM_HORDE] < 0) + { + m_FlagsTimer[BG_TEAM_HORDE] = 0; + RespawnFlag(HORDE, true); + } + } + if(m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) + { + m_FlagsDropTimer[BG_TEAM_HORDE] -= diff; + + if(m_FlagsDropTimer[BG_TEAM_HORDE] < 0) + { + m_FlagsDropTimer[BG_TEAM_HORDE] = 0; + RespawnFlagAfterDrop(HORDE); + } + } + } +} + +void BattleGroundWS::AddPlayer(Player *plr) +{ + BattleGround::AddPlayer(plr); + //create score and add it to map, default values are set in constructor + BattleGroundWGScore* sc = new BattleGroundWGScore; + + m_PlayerScores[plr->GetGUID()] = sc; +} + +void BattleGroundWS::RespawnFlag(uint32 Team, bool captured) +{ + if(Team == ALLIANCE) + { + sLog.outDebug("Respawn Alliance flag"); + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE; + } + else + { + sLog.outDebug("Respawn Horde flag"); + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE; + } + + if(captured) + { + //when map_update will be allowed for battlegrounds this code will be useless + SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); + SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); + SendMessageToAll(GetMangosString(LANG_BG_WS_F_PLACED)); + PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED); // flag respawned sound... + } +} + +void BattleGroundWS::RespawnFlagAfterDrop(uint32 team) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + RespawnFlag(team,false); + if(team == ALLIANCE) + { + SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); + SendMessageToAll(GetMangosString(LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED)); + } + else + { + SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); + SendMessageToAll(GetMangosString(LANG_BG_WS_HORDE_FLAG_RESPAWNED)); + } + + PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED); + + GameObject *obj = HashMapHolder::Find(GetDroppedFlagGUID(team)); + if(obj) + obj->Delete(); + else + sLog.outError("unknown droped flag bg, guid: %u",GetDroppedFlagGUID(team)); + + SetDroppedFlagGUID(0,team); +} + +void BattleGroundWS::EventPlayerCapturedFlag(Player *Source) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + uint8 type = 0; + uint32 winner = 0; + const char *message = ""; + + //TODO FIX reputation and honor gains for low level players! + + Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); + if(Source->GetTeam() == ALLIANCE) + { + if (!this->IsHordeFlagPickedup()) + return; + SetHordeFlagPicker(0); // must be before aura remove to prevent 2 events (drop+capture) at the same time + // horde flag in base (but not respawned yet) + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; + // Drop Horde Flag from Player + Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); + message = GetMangosString(LANG_BG_WS_CAPTURED_HF); + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + if(GetTeamScore(ALLIANCE) < BG_WS_MAX_TEAM_SCORE) + AddPoint(ALLIANCE, 1); + PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE); + RewardReputationToTeam(890, 35, ALLIANCE); // +35 reputation + RewardHonorToTeam(40, ALLIANCE); // +40 bonushonor + } + else + { + if (!this->IsAllianceFlagPickedup()) + return; + SetAllianceFlagPicker(0); // must be before aura remove to prevent 2 events (drop+capture) at the same time + // alliance flag in base (but not respawned yet) + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_WAIT_RESPAWN; + // Drop Alliance Flag from Player + Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); + message = GetMangosString(LANG_BG_WS_CAPTURED_AF); + type = CHAT_MSG_BG_SYSTEM_HORDE; + if(GetTeamScore(HORDE) < BG_WS_MAX_TEAM_SCORE) + AddPoint(HORDE, 1); + PlaySoundToAll(BG_WS_SOUND_FLAG_CAPTURED_HORDE); + RewardReputationToTeam(889, 35, HORDE); // +35 reputation + RewardHonorToTeam(40, HORDE); // +40 bonushonor + } + + SpawnBGObject(BG_WS_OBJECT_H_FLAG, BG_WS_FLAG_RESPAWN_TIME); + SpawnBGObject(BG_WS_OBJECT_A_FLAG, BG_WS_FLAG_RESPAWN_TIME); + + WorldPacket data; + ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); + SendPacketToAll(&data); + + UpdateFlagState(Source->GetTeam(), 1); // flag state none + UpdateTeamScore(Source->GetTeam()); + // only flag capture should be updated + UpdatePlayerScore(Source, SCORE_FLAG_CAPTURES, 1); // +1 flag captures... + + if(GetTeamScore(ALLIANCE) == BG_WS_MAX_TEAM_SCORE) + winner = ALLIANCE; + + if(GetTeamScore(HORDE) == BG_WS_MAX_TEAM_SCORE) + winner = HORDE; + + if(winner) + { + UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 0); + UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 0); + UpdateWorldState(BG_WS_FLAG_STATE_ALLIANCE, 1); + UpdateWorldState(BG_WS_FLAG_STATE_HORDE, 1); + + EndBattleGround(winner); + } + else + { + m_FlagsTimer[GetTeamIndexByTeamId(Source->GetTeam()) ? 0 : 1] = BG_WS_FLAG_RESPAWN_TIME; + } +} + +void BattleGroundWS::EventPlayerDroppedFlag(Player *Source) +{ + // Drop allowed in any BG state + + const char *message = ""; + uint8 type = 0; + bool set = false; + + if(Source->GetTeam() == ALLIANCE) + { + if(!this->IsHordeFlagPickedup()) + return; + if(GetHordeFlagPickerGUID() == Source->GetGUID()) + { + SetHordeFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_WS_SPELL_WARSONG_FLAG); + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_GROUND; + message = GetMangosString(LANG_BG_WS_DROPPED_HF); + type = CHAT_MSG_BG_SYSTEM_HORDE; + Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG_DROPPED, true); + set = true; + } + } + else + { + if(!this->IsAllianceFlagPickedup()) + return; + if(GetAllianceFlagPickerGUID() == Source->GetGUID()) + { + SetAllianceFlagPicker(0); + Source->RemoveAurasDueToSpell(BG_WS_SPELL_SILVERWING_FLAG); + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_GROUND; + message = GetMangosString(LANG_BG_WS_DROPPED_AF); + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG_DROPPED, true); + set = true; + } + } + + if (set) + { + Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true); + UpdateFlagState(Source->GetTeam(), 1); + + WorldPacket data; + ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); + SendPacketToAll(&data); + + if(Source->GetTeam() == ALLIANCE) + UpdateWorldState(BG_WS_FLAG_UNK_HORDE, uint32(-1)); + else + UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, uint32(-1)); + + m_FlagsDropTimer[GetTeamIndexByTeamId(Source->GetTeam()) ? 0 : 1] = BG_WS_FLAG_DROP_TIME; + } +} + +void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + const char *message; + uint8 type = 0; + + //alliance flag picked up from base + if(Source->GetTeam() == HORDE && this->GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_BASE + && this->m_BgObjects[BG_WS_OBJECT_A_FLAG] == target_obj->GetGUID()) + { + message = GetMangosString(LANG_BG_WS_PICKEDUP_AF); + type = CHAT_MSG_BG_SYSTEM_HORDE; + PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP); + SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_ONE_DAY); + SetAllianceFlagPicker(Source->GetGUID()); + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER; + //update world state to show correct flag carrier + UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER); + UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 1); + Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG, true); + } + + //horde flag picked up from base + if (Source->GetTeam() == ALLIANCE && this->GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_BASE + && this->m_BgObjects[BG_WS_OBJECT_H_FLAG] == target_obj->GetGUID()) + { + message = GetMangosString(LANG_BG_WS_PICKEDUP_HF); + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP); + SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_ONE_DAY); + SetHordeFlagPicker(Source->GetGUID()); + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER; + //update world state to show correct flag carrier + UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER); + UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 1); + Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG, true); + } + + //Alliance flag on ground(not in base) (returned or picked up again from ground!) + if(this->GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10)) + { + if(Source->GetTeam() == ALLIANCE) + { + message = GetMangosString(LANG_BG_WS_RETURNED_AF); + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + UpdateFlagState(HORDE, BG_WS_FLAG_STATE_WAIT_RESPAWN); + RespawnFlag(ALLIANCE, false); + SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_IMMEDIATELY); + PlaySoundToAll(BG_WS_SOUND_FLAG_RETURNED); + UpdatePlayerScore(Source, SCORE_FLAG_RETURNS, 1); + } + else + { + message = GetMangosString(LANG_BG_WS_PICKEDUP_AF); + type = CHAT_MSG_BG_SYSTEM_HORDE; + PlaySoundToAll(BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP); + SpawnBGObject(BG_WS_OBJECT_A_FLAG, RESPAWN_ONE_DAY); + SetAllianceFlagPicker(Source->GetGUID()); + Source->CastSpell(Source, BG_WS_SPELL_SILVERWING_FLAG, true); + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_PLAYER; + UpdateFlagState(HORDE, BG_WS_FLAG_STATE_ON_PLAYER); + UpdateWorldState(BG_WS_FLAG_UNK_ALLIANCE, 1); + } + //called in HandleGameObjectUseOpcode: + //target_obj->Delete(); + } + + //Horde flag on ground(not in base) (returned or picked up again) + if(this->GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10)) + { + if(Source->GetTeam() == HORDE) + { + message = GetMangosString(LANG_BG_WS_RETURNED_HF); + type = CHAT_MSG_BG_SYSTEM_HORDE; + UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_WAIT_RESPAWN); + RespawnFlag(HORDE, false); + SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY); + PlaySoundToAll(BG_WS_SOUND_FLAG_RETURNED); + UpdatePlayerScore(Source, SCORE_FLAG_RETURNS, 1); + } + else + { + message = GetMangosString(LANG_BG_WS_PICKEDUP_HF); + type = CHAT_MSG_BG_SYSTEM_ALLIANCE; + PlaySoundToAll(BG_WS_SOUND_HORDE_FLAG_PICKED_UP); + SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_ONE_DAY); + SetHordeFlagPicker(Source->GetGUID()); + Source->CastSpell(Source, BG_WS_SPELL_WARSONG_FLAG, true); + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_PLAYER; + UpdateFlagState(ALLIANCE, BG_WS_FLAG_STATE_ON_PLAYER); + UpdateWorldState(BG_WS_FLAG_UNK_HORDE, 1); + } + //called in HandleGameObjectUseOpcode: + //target_obj->Delete(); + } + + if (!type) + return; + + WorldPacket data; + ChatHandler::FillMessageData(&data, Source->GetSession(), type, LANG_UNIVERSAL, NULL, Source->GetGUID(), message, NULL); + SendPacketToAll(&data); + Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); +} + +void BattleGroundWS::RemovePlayer(Player *plr, uint64 guid) +{ + // sometimes flag aura not removed :( + if(IsAllianceFlagPickedup() && m_FlagKeepers[BG_TEAM_ALLIANCE] == guid) + { + if(!plr) + { + sLog.outError("BattleGroundWS: Removing offline player who has the FLAG!!"); + this->SetAllianceFlagPicker(0); + this->RespawnFlag(ALLIANCE, false); + } + else + this->EventPlayerDroppedFlag(plr); + } + if(IsHordeFlagPickedup() && m_FlagKeepers[BG_TEAM_HORDE] == guid) + { + if(!plr) + { + sLog.outError("BattleGroundWS: Removing offline player who has the FLAG!!"); + this->SetHordeFlagPicker(0); + this->RespawnFlag(HORDE, false); + } + else + this->EventPlayerDroppedFlag(plr); + } +} + +void BattleGroundWS::UpdateFlagState(uint32 team, uint32 value) +{ + if(team == ALLIANCE) + UpdateWorldState(BG_WS_FLAG_STATE_ALLIANCE, value); + else + UpdateWorldState(BG_WS_FLAG_STATE_HORDE, value); +} + +void BattleGroundWS::UpdateTeamScore(uint32 team) +{ + if(team == ALLIANCE) + UpdateWorldState(BG_WS_FLAG_CAPTURES_ALLIANCE, GetTeamScore(team)); + else + UpdateWorldState(BG_WS_FLAG_CAPTURES_HORDE, GetTeamScore(team)); +} + +void BattleGroundWS::HandleAreaTrigger(Player *Source, uint32 Trigger) +{ + // this is wrong way to implement these things. On official it done by gameobject spell cast. + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + //uint32 SpellId = 0; + //uint64 buff_guid = 0; + switch(Trigger) + { + case 3686: // Alliance elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in BattleGround::Update(). + //buff_guid = m_BgObjects[BG_WS_OBJECT_SPEEDBUFF_1]; + break; + case 3687: // Horde elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in BattleGround::Update(). + //buff_guid = m_BgObjects[BG_WS_OBJECT_SPEEDBUFF_2]; + break; + case 3706: // Alliance elixir of regeneration spawn + //buff_guid = m_BgObjects[BG_WS_OBJECT_REGENBUFF_1]; + break; + case 3708: // Horde elixir of regeneration spawn + //buff_guid = m_BgObjects[BG_WS_OBJECT_REGENBUFF_2]; + break; + case 3707: // Alliance elixir of berserk spawn + //buff_guid = m_BgObjects[BG_WS_OBJECT_BERSERKBUFF_1]; + break; + case 3709: // Horde elixir of berserk spawn + //buff_guid = m_BgObjects[BG_WS_OBJECT_BERSERKBUFF_2]; + break; + case 3646: // Alliance Flag spawn + if(m_FlagState[BG_TEAM_HORDE] && !m_FlagState[BG_TEAM_ALLIANCE]) + if(GetHordeFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source); + break; + case 3647: // Horde Flag spawn + if(m_FlagState[BG_TEAM_ALLIANCE] && !m_FlagState[BG_TEAM_HORDE]) + if(GetAllianceFlagPickerGUID() == Source->GetGUID()) + EventPlayerCapturedFlag(Source); + break; + case 3649: // unk1 + case 3688: // unk2 + case 4628: // unk3 + case 4629: // unk4 + break; + default: + sLog.outError("WARNING: Unhandled AreaTrigger in Battleground: %u", Trigger); + Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger); + break; + } + + //if(buff_guid) + // HandleTriggerBuff(buff_guid,Source); +} + +bool BattleGroundWS::SetupBattleGround() +{ + // flags + if( !AddObject(BG_WS_OBJECT_A_FLAG, BG_OBJECT_A_FLAG_WS_ENTRY, 1540.423f, 1481.325f, 351.8284f, 3.089233f, 0, 0, 0.9996573f, 0.02617699f, BG_WS_FLAG_RESPAWN_TIME/1000) + || !AddObject(BG_WS_OBJECT_H_FLAG, BG_OBJECT_H_FLAG_WS_ENTRY, 916.0226f, 1434.405f, 345.413f, 0.01745329f, 0, 0, 0.008726535f, 0.9999619f, BG_WS_FLAG_RESPAWN_TIME/1000) + // buffs + || !AddObject(BG_WS_OBJECT_SPEEDBUFF_1, BG_OBJECTID_SPEEDBUFF_ENTRY, 1449.93f, 1470.71f, 342.6346f, -1.64061f, 0, 0, 0.7313537f, -0.6819983f, BUFF_RESPAWN_TIME) + || !AddObject(BG_WS_OBJECT_SPEEDBUFF_2, BG_OBJECTID_SPEEDBUFF_ENTRY, 1005.171f, 1447.946f, 335.9032f, 1.64061f, 0, 0, 0.7313537f, 0.6819984f, BUFF_RESPAWN_TIME) + || !AddObject(BG_WS_OBJECT_REGENBUFF_1, BG_OBJECTID_REGENBUFF_ENTRY, 1317.506f, 1550.851f, 313.2344f, -0.2617996f, 0, 0, 0.1305263f, -0.9914448f, BUFF_RESPAWN_TIME) + || !AddObject(BG_WS_OBJECT_REGENBUFF_2, BG_OBJECTID_REGENBUFF_ENTRY, 1110.451f, 1353.656f, 316.5181f, -0.6806787f, 0, 0, 0.333807f, -0.9426414f, BUFF_RESPAWN_TIME) + || !AddObject(BG_WS_OBJECT_BERSERKBUFF_1, BG_OBJECTID_BERSERKERBUFF_ENTRY, 1320.09f, 1378.79f, 314.7532f, 1.186824f, 0, 0, 0.5591929f, 0.8290376f, BUFF_RESPAWN_TIME) + || !AddObject(BG_WS_OBJECT_BERSERKBUFF_2, BG_OBJECTID_BERSERKERBUFF_ENTRY, 1139.688f, 1560.288f, 306.8432f, -2.443461f, 0, 0, 0.9396926f, -0.3420201f, BUFF_RESPAWN_TIME) + // alliance gates + || !AddObject(BG_WS_OBJECT_DOOR_A_1, BG_OBJECT_DOOR_A_1_WS_ENTRY, 1503.335f, 1493.466f, 352.1888f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_A_2, BG_OBJECT_DOOR_A_2_WS_ENTRY, 1492.478f, 1457.912f, 342.9689f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_A_3, BG_OBJECT_DOOR_A_3_WS_ENTRY, 1468.503f, 1494.357f, 351.8618f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_A_4, BG_OBJECT_DOOR_A_4_WS_ENTRY, 1471.555f, 1458.778f, 362.6332f, 3.115414f, 0, 0, 0.9999143f, 0.01308903f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_A_5, BG_OBJECT_DOOR_A_5_WS_ENTRY, 1492.347f, 1458.34f, 342.3712f, -0.03490669f, 0, 0, 0.01745246f, -0.9998477f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_A_6, BG_OBJECT_DOOR_A_6_WS_ENTRY, 1503.466f, 1493.367f, 351.7352f, -0.03490669f, 0, 0, 0.01745246f, -0.9998477f, RESPAWN_IMMEDIATELY) + // horde gates + || !AddObject(BG_WS_OBJECT_DOOR_H_1, BG_OBJECT_DOOR_H_1_WS_ENTRY, 949.1663f, 1423.772f, 345.6241f, -0.5756807f, -0.01673368f, -0.004956111f, -0.2839723f, 0.9586737f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_H_2, BG_OBJECT_DOOR_H_2_WS_ENTRY, 953.0507f, 1459.842f, 340.6526f, -1.99662f, -0.1971825f, 0.1575096f, -0.8239487f, 0.5073641f, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_H_3, BG_OBJECT_DOOR_H_3_WS_ENTRY, 949.9523f, 1422.751f, 344.9273f, 0.0f, 0, 0, 0, 1, RESPAWN_IMMEDIATELY) + || !AddObject(BG_WS_OBJECT_DOOR_H_4, BG_OBJECT_DOOR_H_4_WS_ENTRY, 950.7952f, 1459.583f, 342.1523f, 0.05235988f, 0, 0, 0.02617695f, 0.9996573f, RESPAWN_IMMEDIATELY) + ) + { + sLog.outErrorDb("BatteGroundWS: Failed to spawn some object BattleGround not created!"); + return false; + } + + WorldSafeLocsEntry const *sg = sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_ALLIANCE); + if(!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, ALLIANCE)) + { + sLog.outErrorDb("BatteGroundWS: Failed to spawn Alliance spirit guide! BattleGround not created!"); + return false; + } + + sg = sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_MAIN_HORDE); + if(!sg || !AddSpiritGuide(WS_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, HORDE)) + { + sLog.outErrorDb("BatteGroundWS: Failed to spawn Horde spirit guide! BattleGround not created!"); + return false; + } + + sLog.outDebug("BatteGroundWS: BG objects and spirit guides spawned"); + + return true; +} + +void BattleGroundWS::ResetBGSubclass() +{ + m_FlagKeepers[BG_TEAM_ALLIANCE] = 0; + m_FlagKeepers[BG_TEAM_HORDE] = 0; + m_DroppedFlagGUID[BG_TEAM_ALLIANCE] = 0; + m_DroppedFlagGUID[BG_TEAM_HORDE] = 0; + m_FlagState[BG_TEAM_ALLIANCE] = BG_WS_FLAG_STATE_ON_BASE; + m_FlagState[BG_TEAM_HORDE] = BG_WS_FLAG_STATE_ON_BASE; + m_TeamScores[BG_TEAM_ALLIANCE] = 0; + m_TeamScores[BG_TEAM_HORDE] = 0; + + /* Spirit nodes is static at this BG and then not required deleting at BG reset. + if(m_BgCreatures[WS_SPIRIT_MAIN_ALLIANCE]) + DelCreature(WS_SPIRIT_MAIN_ALLIANCE); + + if(m_BgCreatures[WS_SPIRIT_MAIN_HORDE]) + DelCreature(WS_SPIRIT_MAIN_HORDE); + */ +} + +void BattleGroundWS::HandleKillPlayer(Player *player, Player *killer) +{ + if(GetStatus() != STATUS_IN_PROGRESS) + return; + + EventPlayerDroppedFlag(player); + + BattleGround::HandleKillPlayer(player, killer); +} + +void BattleGroundWS::UpdatePlayerScore(Player *Source, uint32 type, uint32 value) +{ + + std::map::iterator itr = m_PlayerScores.find(Source->GetGUID()); + + if(itr == m_PlayerScores.end()) // player not found + return; + + switch(type) + { + case SCORE_FLAG_CAPTURES: // flags captured + ((BattleGroundWGScore*)itr->second)->FlagCaptures += value; + break; + case SCORE_FLAG_RETURNS: // flags returned + ((BattleGroundWGScore*)itr->second)->FlagReturns += value; + break; + default: + BattleGround::UpdatePlayerScore(Source, type, value); + break; + } +} + +void BattleGroundWS::FillInitialWorldStates(WorldPacket& data) +{ + data << uint32(BG_WS_FLAG_CAPTURES_ALLIANCE) << uint32(GetTeamScore(ALLIANCE)); + data << uint32(BG_WS_FLAG_CAPTURES_HORDE) << uint32(GetTeamScore(HORDE)); + + if(m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_GROUND) + data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(-1); + else if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) + data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(1); + else + data << uint32(BG_WS_FLAG_UNK_ALLIANCE) << uint32(0); + + if(m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_GROUND) + data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(-1); + else if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) + data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(1); + else + data << uint32(BG_WS_FLAG_UNK_HORDE) << uint32(0); + + data << uint32(BG_WS_FLAG_CAPTURES_MAX) << uint32(BG_WS_MAX_TEAM_SCORE); + + if (m_FlagState[BG_TEAM_HORDE] == BG_WS_FLAG_STATE_ON_PLAYER) + data << uint32(BG_WS_FLAG_STATE_HORDE) << uint32(2); + else + data << uint32(BG_WS_FLAG_STATE_HORDE) << uint32(1); + + if (m_FlagState[BG_TEAM_ALLIANCE] == BG_WS_FLAG_STATE_ON_PLAYER) + data << uint32(BG_WS_FLAG_STATE_ALLIANCE) << uint32(2); + else + data << uint32(BG_WS_FLAG_STATE_ALLIANCE) << uint32(1); + +} diff --git a/src/game/BattleGroundWS.h b/src/game/BattleGroundWS.h new file mode 100644 index 00000000000..d9f2b7f6142 --- /dev/null +++ b/src/game/BattleGroundWS.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __BATTLEGROUNDWS_H +#define __BATTLEGROUNDWS_H + +#include "BattleGround.h" + +#define BG_WS_MAX_TEAM_SCORE 3 +#define BG_WS_FLAG_RESPAWN_TIME 23000 +#define BG_WS_FLAG_DROP_TIME 10000 + +enum BG_WS_Sound +{ + BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, + BG_WS_SOUND_FLAG_CAPTURED_HORDE = 8213, + BG_WS_SOUND_FLAG_PLACED = 8232, + BG_WS_SOUND_FLAG_RETURNED = 8192, + BG_WS_SOUND_HORDE_FLAG_PICKED_UP = 8212, + BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP = 8174, + BG_WS_SOUND_FLAGS_RESPAWNED = 8232 +}; + +enum BG_WS_SpellId +{ + BG_WS_SPELL_WARSONG_FLAG = 23333, + BG_WS_SPELL_WARSONG_FLAG_DROPPED = 23334, + BG_WS_SPELL_SILVERWING_FLAG = 23335, + BG_WS_SPELL_SILVERWING_FLAG_DROPPED = 23336 +}; + +enum BG_WS_WorldStates +{ + BG_WS_FLAG_UNK_ALLIANCE = 1545, + BG_WS_FLAG_UNK_HORDE = 1546, +// FLAG_UNK = 1547, + BG_WS_FLAG_CAPTURES_ALLIANCE = 1581, + BG_WS_FLAG_CAPTURES_HORDE = 1582, + BG_WS_FLAG_CAPTURES_MAX = 1601, + BG_WS_FLAG_STATE_HORDE = 2338, + BG_WS_FLAG_STATE_ALLIANCE = 2339 +}; + +enum BG_WS_ObjectTypes +{ + BG_WS_OBJECT_DOOR_A_1 = 0, + BG_WS_OBJECT_DOOR_A_2 = 1, + BG_WS_OBJECT_DOOR_A_3 = 2, + BG_WS_OBJECT_DOOR_A_4 = 3, + BG_WS_OBJECT_DOOR_A_5 = 4, + BG_WS_OBJECT_DOOR_A_6 = 5, + BG_WS_OBJECT_DOOR_H_1 = 6, + BG_WS_OBJECT_DOOR_H_2 = 7, + BG_WS_OBJECT_DOOR_H_3 = 8, + BG_WS_OBJECT_DOOR_H_4 = 9, + BG_WS_OBJECT_A_FLAG = 10, + BG_WS_OBJECT_H_FLAG = 11, + BG_WS_OBJECT_SPEEDBUFF_1 = 12, + BG_WS_OBJECT_SPEEDBUFF_2 = 13, + BG_WS_OBJECT_REGENBUFF_1 = 14, + BG_WS_OBJECT_REGENBUFF_2 = 15, + BG_WS_OBJECT_BERSERKBUFF_1 = 16, + BG_WS_OBJECT_BERSERKBUFF_2 = 17, + BG_WS_OBJECT_MAX = 18 +}; + +enum BG_WS_ObjectEntry +{ + BG_OBJECT_DOOR_A_1_WS_ENTRY = 179918, + BG_OBJECT_DOOR_A_2_WS_ENTRY = 179919, + BG_OBJECT_DOOR_A_3_WS_ENTRY = 179920, + BG_OBJECT_DOOR_A_4_WS_ENTRY = 179921, + BG_OBJECT_DOOR_A_5_WS_ENTRY = 180322, + BG_OBJECT_DOOR_A_6_WS_ENTRY = 180322, + BG_OBJECT_DOOR_H_1_WS_ENTRY = 179916, + BG_OBJECT_DOOR_H_2_WS_ENTRY = 179917, + BG_OBJECT_DOOR_H_3_WS_ENTRY = 180322, + BG_OBJECT_DOOR_H_4_WS_ENTRY = 180322, + BG_OBJECT_A_FLAG_WS_ENTRY = 179830, + BG_OBJECT_H_FLAG_WS_ENTRY = 179831 +}; + +enum BG_WS_FlagState +{ + BG_WS_FLAG_STATE_ON_BASE = 0, + BG_WS_FLAG_STATE_WAIT_RESPAWN = 1, + BG_WS_FLAG_STATE_ON_PLAYER = 2, + BG_WS_FLAG_STATE_ON_GROUND = 3 +}; + +enum BG_WS_Graveyards +{ + WS_GRAVEYARD_MAIN_ALLIANCE = 771, + WS_GRAVEYARD_MAIN_HORDE = 772 +}; + +enum BG_WS_CreatureTypes +{ + WS_SPIRIT_MAIN_ALLIANCE = 0, + WS_SPIRIT_MAIN_HORDE = 1, + + BG_CREATURES_MAX_WS = 2 +}; + +class BattleGroundWGScore : public BattleGroundScore +{ + public: + BattleGroundWGScore() : FlagCaptures(0), FlagReturns(0) {}; + virtual ~BattleGroundWGScore() {}; + uint32 FlagCaptures; + uint32 FlagReturns; +}; + +class BattleGroundWS : public BattleGround +{ + friend class BattleGroundMgr; + + public: + /* Construction */ + BattleGroundWS(); + ~BattleGroundWS(); + void Update(time_t diff); + + /* inherited from BattlegroundClass */ + virtual void AddPlayer(Player *plr); + + /* BG Flags */ + uint64 GetAllianceFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_ALLIANCE]; } + uint64 GetHordeFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_HORDE]; } + void SetAllianceFlagPicker(uint64 guid) { m_FlagKeepers[BG_TEAM_ALLIANCE] = guid; } + void SetHordeFlagPicker(uint64 guid) { m_FlagKeepers[BG_TEAM_HORDE] = guid; } + bool IsAllianceFlagPickedup() const { return m_FlagKeepers[BG_TEAM_ALLIANCE] != 0; } + bool IsHordeFlagPickedup() const { return m_FlagKeepers[BG_TEAM_HORDE] != 0; } + void RespawnFlag(uint32 Team, bool captured); + void RespawnFlagAfterDrop(uint32 Team); + uint8 GetFlagState(uint32 team) { return m_FlagState[GetTeamIndexByTeamId(team)]; } + + /* Battleground Events */ + virtual void EventPlayerDroppedFlag(Player *Source); + virtual void EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj); + virtual void EventPlayerCapturedFlag(Player *Source); + + void RemovePlayer(Player *plr, uint64 guid); + void HandleAreaTrigger(Player *Source, uint32 Trigger); + void HandleKillPlayer(Player *player, Player *killer); + bool SetupBattleGround(); + virtual void ResetBGSubclass(); + + void UpdateFlagState(uint32 team, uint32 value); + void UpdateTeamScore(uint32 team); + void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); + void SetDroppedFlagGUID(uint64 guid, uint32 TeamID) { m_DroppedFlagGUID[GetTeamIndexByTeamId(TeamID)] = guid;} + uint64 GetDroppedFlagGUID(uint32 TeamID) { return m_DroppedFlagGUID[GetTeamIndexByTeamId(TeamID)];} + virtual void FillInitialWorldStates(WorldPacket& data); + + /* Scorekeeping */ + uint32 GetTeamScore(uint32 TeamID) const { return m_TeamScores[GetTeamIndexByTeamId(TeamID)]; } + void AddPoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] += Points; } + void SetTeamPoint(uint32 TeamID, uint32 Points = 0) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] = Points; } + void RemovePoint(uint32 TeamID, uint32 Points = 1) { m_TeamScores[GetTeamIndexByTeamId(TeamID)] -= Points; } + + private: + uint64 m_FlagKeepers[2]; // 0 - alliance, 1 - horde + uint64 m_DroppedFlagGUID[2]; + uint8 m_FlagState[2]; // for checking flag state + uint32 m_TeamScores[2]; + int32 m_FlagsTimer[2]; + int32 m_FlagsDropTimer[2]; +}; +#endif diff --git a/src/game/Cell.h b/src/game/Cell.h new file mode 100644 index 00000000000..873debabb14 --- /dev/null +++ b/src/game/Cell.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CELL_H +#define MANGOS_CELL_H + +#include "GameSystem/TypeContainer.h" +#include "GameSystem/TypeContainerVisitor.h" +#include "GridDefines.h" +#include + +class Map; + +enum District +{ + UPPER_DISTRICT = 1, + LOWER_DISTRICT = 1 << 1, + LEFT_DISTRICT = 1 << 2, + RIGHT_DISTRICT = 1 << 3, + CENTER_DISTRICT = 1 << 4, + UPPER_LEFT_DISTRICT = (UPPER_DISTRICT | LEFT_DISTRICT), + UPPER_RIGHT_DISTRICT = (UPPER_DISTRICT | RIGHT_DISTRICT), + LOWER_LEFT_DISTRICT = (LOWER_DISTRICT | LEFT_DISTRICT), + LOWER_RIGHT_DISTRICT = (LOWER_DISTRICT | RIGHT_DISTRICT), + ALL_DISTRICT = (UPPER_DISTRICT | LOWER_DISTRICT | LEFT_DISTRICT | RIGHT_DISTRICT | CENTER_DISTRICT) +}; + +template struct CellLock; + +struct MANGOS_DLL_DECL Cell +{ + Cell() { data.All = 0; } + Cell(const Cell &cell) { data.All = cell.data.All; } + explicit Cell(CellPair const& p); + + void operator|=(Cell &cell) + { + data.Part.reserved = 0; + cell.data.Part.reserved = 0; + uint32 x, y, old_x, old_y; + Compute(x, y); + cell.Compute(old_x, old_y); + + if( std::abs(int(x-old_x)) > 1 || std::abs(int(y-old_y)) > 1) + { + data.Part.reserved = ALL_DISTRICT; + cell.data.Part.reserved = ALL_DISTRICT; + return; + } + + if( x < old_x ) + { + data.Part.reserved |= LEFT_DISTRICT; + cell.data.Part.reserved |= RIGHT_DISTRICT; + } + else if( old_x < x ) + { + data.Part.reserved |= RIGHT_DISTRICT; + cell.data.Part.reserved |= LEFT_DISTRICT; + } + if( y < old_y ) + { + data.Part.reserved |= UPPER_DISTRICT; + cell.data.Part.reserved |= LOWER_DISTRICT; + } + else if( old_y < y ) + { + data.Part.reserved |= LOWER_DISTRICT; + cell.data.Part.reserved |= UPPER_DISTRICT; + } + } + + void Compute(uint32 &x, uint32 &y) const + { + x = data.Part.grid_x*MAX_NUMBER_OF_CELLS + data.Part.cell_x; + y = data.Part.grid_y*MAX_NUMBER_OF_CELLS + data.Part.cell_y; + } + + inline bool DiffCell(const Cell &cell) const + { + return( data.Part.cell_x != cell.data.Part.cell_x || + data.Part.cell_y != cell.data.Part.cell_y ); + } + + inline bool DiffGrid(const Cell &cell) const + { + return( data.Part.grid_x != cell.data.Part.grid_x || + data.Part.grid_y != cell.data.Part.grid_y ); + } + + uint32 CellX() const { return data.Part.cell_x; } + uint32 CellY() const { return data.Part.cell_y; } + uint32 GridX() const { return data.Part.grid_x; } + uint32 GridY() const { return data.Part.grid_y; } + bool NoCreate() const { return data.Part.nocreate; } + void SetNoCreate() { data.Part.nocreate = 1; } + + CellPair cellPair() const + { + return CellPair( + data.Part.grid_x*MAX_NUMBER_OF_CELLS+data.Part.cell_x, + data.Part.grid_y*MAX_NUMBER_OF_CELLS+data.Part.cell_y); + } + + Cell& operator=(const Cell &cell) + { + this->data.All = cell.data.All; + return *this; + } + + bool operator==(const Cell &cell) const { return (data.All == cell.data.All); } + bool operator!=(const Cell &cell) const { return !operator==(cell); } + union + { + struct + { + unsigned grid_x : 6; + unsigned grid_y : 6; + unsigned cell_x : 4; + unsigned cell_y : 4; + unsigned nocreate : 1; + unsigned reserved : 11; + } Part; + uint32 All; + } data; + + template void Visit(const CellLock &, TypeContainerVisitor &visitor, Map &) const; +}; + +template +struct MANGOS_DLL_DECL CellLock +{ + const Cell& i_cell; + const CellPair &i_cellPair; + CellLock(const Cell &c, const CellPair &p) : i_cell(c), i_cellPair(p) {} + CellLock(const CellLock &cell) : i_cell(cell.i_cell), i_cellPair(cell.i_cellPair) {} + const Cell* operator->(void) const { return &i_cell; } + const Cell* operator->(void) { return &i_cell; } + operator const Cell &(void) const { return i_cell; } + CellLock& operator=(const CellLock &cell) + { + this->~CellLock(); + new (this) CellLock(cell); + return *this; + } +}; +#endif diff --git a/src/game/CellImpl.h b/src/game/CellImpl.h new file mode 100644 index 00000000000..72c41e4b3c4 --- /dev/null +++ b/src/game/CellImpl.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CELLIMPL_H +#define MANGOS_CELLIMPL_H + +#include "Cell.h" +#include "Map.h" +#include + +inline Cell::Cell(CellPair const& p) +{ + data.Part.grid_x = p.x_coord / MAX_NUMBER_OF_CELLS; + data.Part.grid_y = p.y_coord / MAX_NUMBER_OF_CELLS; + data.Part.cell_x = p.x_coord % MAX_NUMBER_OF_CELLS; + data.Part.cell_y = p.y_coord % MAX_NUMBER_OF_CELLS; + data.Part.nocreate = 0; + data.Part.reserved = 0; +} + +template +inline void +Cell::Visit(const CellLock &l, TypeContainerVisitor &visitor, Map &m) const +{ + const CellPair &standing_cell = l.i_cellPair; + if (standing_cell.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || standing_cell.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP) + return; + + uint16 district = (District)this->data.Part.reserved; + if(district == CENTER_DISTRICT) + { + m.Visit(l, visitor); + return; + } + + // set up the cell range based on the district + // the overloaded operators handle range checking + CellPair begin_cell = standing_cell; + CellPair end_cell = standing_cell; + + switch( district ) + { + case ALL_DISTRICT: + { + begin_cell << 1; begin_cell -= 1; // upper left + end_cell >> 1; end_cell += 1; // lower right + break; + } + case UPPER_LEFT_DISTRICT: + { + begin_cell << 1; begin_cell -= 1; // upper left + break; + } + case UPPER_RIGHT_DISTRICT: + { + begin_cell -= 1; // up + end_cell >> 1; // right + break; + } + case LOWER_LEFT_DISTRICT: + { + begin_cell << 1; // left + end_cell += 1; // down + break; + } + case LOWER_RIGHT_DISTRICT: + { + end_cell >> 1; end_cell += 1; // lower right + break; + } + case LEFT_DISTRICT: + { + begin_cell -= 1; // up + end_cell >> 1; end_cell += 1; // lower right + break; + } + case RIGHT_DISTRICT: + { + begin_cell << 1; begin_cell -= 1; // upper left + end_cell += 1; // down + break; + } + case UPPER_DISTRICT: + { + begin_cell << 1; begin_cell -= 1; // upper left + end_cell >> 1; // right + break; + } + case LOWER_DISTRICT: + { + begin_cell << 1; // left + end_cell >> 1; end_cell += 1; // lower right + break; + } + default: + { + assert( false ); + break; + } + } + + // loop the cell range + for(uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; x++) + { + for(uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; y++) + { + CellPair cell_pair(x,y); + Cell r_zone(cell_pair); + r_zone.data.Part.nocreate = l->data.Part.nocreate; + CellLock lock(r_zone, cell_pair); + m.Visit(lock, visitor); + } + } +} +#endif diff --git a/src/game/Channel.cpp b/src/game/Channel.cpp new file mode 100644 index 00000000000..0fff8c7854b --- /dev/null +++ b/src/game/Channel.cpp @@ -0,0 +1,994 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Channel.h" +#include "ObjectMgr.h" +#include "World.h" +#include "SocialMgr.h" + +Channel::Channel(std::string name, uint32 channel_id) +: m_name(name), m_announce(true), m_moderate(false), m_channelId(channel_id), m_ownerGUID(0), m_password(""), m_flags(0) +{ + // set special flags if built-in channel + ChatChannelsEntry const* ch = GetChannelEntryFor(channel_id); + if(ch) // it's built-in channel + { + channel_id = ch->ChannelID; // built-in channel + m_announce = false; // no join/leave announces + + m_flags |= CHANNEL_FLAG_GENERAL; // for all built-in channels + + if(ch->flags & CHANNEL_DBC_FLAG_TRADE) // for trade channel + m_flags |= CHANNEL_FLAG_TRADE; + + if(ch->flags & CHANNEL_DBC_FLAG_CITY_ONLY2) // for city only channels + m_flags |= CHANNEL_FLAG_CITY; + + if(ch->flags & CHANNEL_DBC_FLAG_LFG) // for LFG channel + m_flags |= CHANNEL_FLAG_LFG; + else // for all other channels + m_flags |= CHANNEL_FLAG_NOT_LFG; + } + else // it's custom channel + { + m_flags |= CHANNEL_FLAG_CUSTOM; + } +} + +void Channel::Join(uint64 p, const char *pass) +{ + WorldPacket data; + if(IsOn(p)) + { + if(!IsConstant()) // non send error message for built-in channels + { + MakePlayerAlreadyMember(&data, p); + SendToOne(&data, p); + } + return; + } + + if(IsBanned(p)) + { + MakeBanned(&data); + SendToOne(&data, p); + return; + } + + if(m_password.length() > 0 && strcmp(pass, m_password.c_str())) + { + MakeWrongPassword(&data); + SendToOne(&data, p); + return; + } + + Player *plr = objmgr.GetPlayer(p); + + if(plr) + { + if(HasFlag(CHANNEL_FLAG_LFG) && + sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && plr->GetSession()->GetSecurity() == SEC_PLAYER && + (plr->GetGroup() || plr->m_lookingForGroup.Empty()) ) + { + MakeNotInLfg(&data); + SendToOne(&data, p); + return; + } + + if(plr->GetGuildId() && (GetFlags() == 0x38)) + return; + + plr->JoinedChannel(this); + } + + if(m_announce && (!plr || plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || !sWorld.getConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL) )) + { + MakeJoined(&data, p); + SendToAll(&data); + } + + data.clear(); + + PlayerInfo pinfo; + pinfo.player = p; + pinfo.flags = 0; + players[p] = pinfo; + + MakeYouJoined(&data); + SendToOne(&data, p); + + JoinNotify(p); + + // if no owner first logged will become + if(!IsConstant() && !m_ownerGUID) + { + SetOwner(p, (players.size() > 1 ? true : false)); + players[p].SetModerator(true); + } +} + +void Channel::Leave(uint64 p, bool send) +{ + if(!IsOn(p)) + { + if(send) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + } + else + { + Player *plr = objmgr.GetPlayer(p); + + if(send) + { + WorldPacket data; + MakeYouLeft(&data); + SendToOne(&data, p); + if(plr) + plr->LeftChannel(this); + data.clear(); + } + + bool changeowner = players[p].IsOwner(); + + players.erase(p); + if(m_announce && (!plr || plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || !sWorld.getConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL) )) + { + WorldPacket data; + MakeLeft(&data, p); + SendToAll(&data); + } + + LeaveNotify(p); + + if(changeowner) + { + uint64 newowner = !players.empty() ? players.begin()->second.player : 0; + SetOwner(newowner); + } + } +} + +void Channel::KickOrBan(uint64 good, const char *badname, bool ban) +{ + uint32 sec = 0; + Player *gplr = objmgr.GetPlayer(good); + if(gplr) + sec = gplr->GetSession()->GetSecurity(); + + if(!IsOn(good)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, good); + } + else if(!players[good].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, good); + } + else + { + Player *bad = objmgr.GetPlayer(badname); + if(bad == NULL || !IsOn(bad->GetGUID())) + { + WorldPacket data; + MakePlayerNotFound(&data, badname); + SendToOne(&data, good); + } + else if(sec < SEC_GAMEMASTER && bad->GetGUID() == m_ownerGUID && good != m_ownerGUID) + { + WorldPacket data; + MakeNotOwner(&data); + SendToOne(&data, good); + } + else + { + bool changeowner = (m_ownerGUID == bad->GetGUID()); + + WorldPacket data; + + if(ban && !IsBanned(bad->GetGUID())) + { + banned.push_back(bad->GetGUID()); + MakePlayerBanned(&data, bad->GetGUID(), good); + } + else + MakePlayerKicked(&data, bad->GetGUID(), good); + + SendToAll(&data); + players.erase(bad->GetGUID()); + bad->LeftChannel(this); + + if(changeowner) + { + uint64 newowner = !players.empty() ? good : false; + SetOwner(newowner); + } + } + } +} + +void Channel::UnBan(uint64 good, const char *badname) +{ + uint32 sec = 0; + Player *gplr = objmgr.GetPlayer(good); + if(gplr) + sec = gplr->GetSession()->GetSecurity(); + + if(!IsOn(good)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, good); + } + else if(!players[good].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, good); + } + else + { + Player *bad = objmgr.GetPlayer(badname); + if(bad == NULL || !IsBanned(bad->GetGUID())) + { + WorldPacket data; + MakePlayerNotFound(&data, badname); + SendToOne(&data, good); + } + else + { + banned.remove(bad->GetGUID()); + + WorldPacket data; + MakePlayerUnbanned(&data, bad->GetGUID(), good); + SendToAll(&data); + } + } +} + +void Channel::Password(uint64 p, const char *pass) +{ + uint32 sec = 0; + Player *plr = objmgr.GetPlayer(p); + if(plr) + sec = plr->GetSession()->GetSecurity(); + + if(!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else if(!players[p].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, p); + } + else + { + m_password = pass; + + WorldPacket data; + MakePasswordChanged(&data, p); + SendToAll(&data); + } +} + +void Channel::SetMode(uint64 p, const char *p2n, bool mod, bool set) +{ + uint32 sec = 0; + Player *plr = objmgr.GetPlayer(p); + if(plr) + sec = plr->GetSession()->GetSecurity(); + + if(!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else if(!players[p].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, p); + } + else + { + Player *newp = objmgr.GetPlayer(p2n); + if(!newp) + { + WorldPacket data; + MakePlayerNotFound(&data, p2n); + SendToOne(&data, p); + return; + } + + PlayerInfo inf = players[newp->GetGUID()]; + if(p == m_ownerGUID && newp->GetGUID() == m_ownerGUID && mod) + return; + + if(!IsOn(newp->GetGUID())) + { + WorldPacket data; + MakePlayerNotFound(&data, p2n); + SendToOne(&data, p); + return; + } + + // allow make moderator from another team only if both is GMs + // at this moment this only way to show channel post for GM from another team + if( (plr->GetSession()->GetSecurity() < SEC_GAMEMASTER || newp->GetSession()->GetSecurity() < SEC_GAMEMASTER) && + plr->GetTeam() != newp->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL) ) + { + WorldPacket data; + MakePlayerNotFound(&data, p2n); + SendToOne(&data, p); + return; + } + + if(m_ownerGUID == newp->GetGUID() && m_ownerGUID != p) + { + WorldPacket data; + MakeNotOwner(&data); + SendToOne(&data, p); + return; + } + + if(mod) + SetModerator(newp->GetGUID(), set); + else + SetMute(newp->GetGUID(), set); + } +} + +void Channel::SetOwner(uint64 p, const char *newname) +{ + uint32 sec = 0; + Player *plr = objmgr.GetPlayer(p); + if(plr) + sec = plr->GetSession()->GetSecurity(); + + if(!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + return; + } + + if(sec < SEC_GAMEMASTER && p != m_ownerGUID) + { + WorldPacket data; + MakeNotOwner(&data); + SendToOne(&data, p); + return; + } + + Player *newp = objmgr.GetPlayer(newname); + if(newp == NULL || !IsOn(newp->GetGUID())) + { + WorldPacket data; + MakePlayerNotFound(&data, newname); + SendToOne(&data, p); + return; + } + + if(newp->GetTeam() != plr->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + { + WorldPacket data; + MakePlayerNotFound(&data, newname); + SendToOne(&data, p); + return; + } + + players[newp->GetGUID()].SetModerator(true); + SetOwner(newp->GetGUID()); +} + +void Channel::SendWhoOwner(uint64 p) +{ + if(!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else + { + WorldPacket data; + MakeChannelOwner(&data); + SendToOne(&data, p); + } +} + +void Channel::List(Player* player) +{ + uint64 p = player->GetGUID(); + + if(!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else + { + WorldPacket data(SMSG_CHANNEL_LIST, 1+(GetName().size()+1)+1+4+players.size()*(8+1)); + data << uint8(1); // channel type? + data << GetName(); // channel name + data << uint8(GetFlags()); // channel flags? + + size_t pos = data.wpos(); + data << uint32(0); // size of list, placeholder + + bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST) || player->GetSession()->GetSecurity() > SEC_PLAYER; + + uint32 count = 0; + for(PlayerList::iterator i = players.begin(); i != players.end(); ++i) + { + Player *plr = objmgr.GetPlayer(i->first); + + // PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters + // MODERATOR, GAME MASTER, ADMINISTRATOR can see all + if( plr && ( plr->GetSession()->GetSecurity() == SEC_PLAYER || gmInWhoList && plr->IsVisibleGloballyFor(player) ) ) + { + data << uint64(i->first); + data << uint8(i->second.flags); // flags seems to be changed... + ++count; + } + } + + data.put(pos,count); + + SendToOne(&data, p); + } +} + +void Channel::Announce(uint64 p) +{ + uint32 sec = 0; + Player *plr = objmgr.GetPlayer(p); + if(plr) + sec = plr->GetSession()->GetSecurity(); + + if(!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else if(!players[p].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, p); + } + else + { + m_announce = !m_announce; + + WorldPacket data; + if(m_announce) + MakeAnnouncementsOn(&data, p); + else + MakeAnnouncementsOff(&data, p); + SendToAll(&data); + } +} + +void Channel::Moderate(uint64 p) +{ + uint32 sec = 0; + Player *plr = objmgr.GetPlayer(p); + if(plr) + sec = plr->GetSession()->GetSecurity(); + + if(!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else if(!players[p].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, p); + } + else + { + m_moderate = !m_moderate; + + WorldPacket data; + if(m_moderate) + MakeModerationOn(&data, p); + else + MakeModerationOff(&data, p); + SendToAll(&data); + } +} + +void Channel::Say(uint64 p, const char *what, uint32 lang) +{ + if(!what) + return; + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + lang = LANG_UNIVERSAL; + + uint32 sec = 0; + Player *plr = objmgr.GetPlayer(p); + if(plr) + sec = plr->GetSession()->GetSecurity(); + + if(!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + } + else if(players[p].IsMuted()) + { + WorldPacket data; + MakeMuted(&data); + SendToOne(&data, p); + } + else if(m_moderate && !players[p].IsModerator() && sec < SEC_GAMEMASTER) + { + WorldPacket data; + MakeNotModerator(&data); + SendToOne(&data, p); + } + else + { + uint32 messageLength = strlen(what) + 1; + + WorldPacket data(SMSG_MESSAGECHAT, 1+4+8+4+m_name.size()+1+8+4+messageLength+1); + data << (uint8)CHAT_MSG_CHANNEL; + data << (uint32)lang; + data << p; // 2.1.0 + data << uint32(0); // 2.1.0 + data << m_name; + data << p; + data << messageLength; + data << what; + data << uint8(plr ? plr->chatTag() : 0); + + SendToAll(&data, !players[p].IsModerator() ? p : false); + } +} + +void Channel::Invite(uint64 p, const char *newname) +{ + if(!IsOn(p)) + { + WorldPacket data; + MakeNotMember(&data); + SendToOne(&data, p); + return; + } + + Player *newp = objmgr.GetPlayer(newname); + if(!newp) + { + WorldPacket data; + MakePlayerNotFound(&data, newname); + SendToOne(&data, p); + return; + } + + Player *plr = objmgr.GetPlayer(p); + if (!plr) + return; + + if (newp->GetTeam() != plr->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + { + WorldPacket data; + MakeInviteWrongFaction(&data); + SendToOne(&data, p); + return; + } + + if(IsOn(newp->GetGUID())) + { + WorldPacket data; + MakePlayerAlreadyMember(&data, newp->GetGUID()); + SendToOne(&data, p); + return; + } + + WorldPacket data; + if(!newp->GetSocial()->HasIgnore(GUID_LOPART(p))) + { + MakeInvite(&data, p); + SendToOne(&data, newp->GetGUID()); + data.clear(); + } + MakePlayerInvited(&data, newp->GetGUID()); + SendToOne(&data, p); +} + +void Channel::SetOwner(uint64 guid, bool exclaim) +{ + if(m_ownerGUID) + { + // [] will re-add player after it possible removed + PlayerList::iterator p_itr = players.find(m_ownerGUID); + if(p_itr != players.end()) + p_itr->second.SetOwner(false); + } + + m_ownerGUID = guid; + if(m_ownerGUID) + { + uint8 oldFlag = GetPlayerFlags(m_ownerGUID); + players[m_ownerGUID].SetOwner(true); + + WorldPacket data; + MakeModeChange(&data, m_ownerGUID, oldFlag); + SendToAll(&data); + + if(exclaim) + { + MakeOwnerChanged(&data, m_ownerGUID); + SendToAll(&data); + } + } +} + +void Channel::SendToAll(WorldPacket *data, uint64 p) +{ + for(PlayerList::iterator i = players.begin(); i != players.end(); ++i) + { + Player *plr = objmgr.GetPlayer(i->first); + if(plr) + { + if(!p || !plr->GetSocial()->HasIgnore(GUID_LOPART(p))) + plr->GetSession()->SendPacket(data); + } + } +} + +void Channel::SendToAllButOne(WorldPacket *data, uint64 who) +{ + for(PlayerList::iterator i = players.begin(); i != players.end(); ++i) + { + if(i->first != who) + { + Player *plr = objmgr.GetPlayer(i->first); + if(plr) + plr->GetSession()->SendPacket(data); + } + } +} + +void Channel::SendToOne(WorldPacket *data, uint64 who) +{ + Player *plr = objmgr.GetPlayer(who); + if(plr) + plr->GetSession()->SendPacket(data); +} + +void Channel::Voice(uint64 guid1, uint64 guid2) +{ + +} + +void Channel::DeVoice(uint64 guid1, uint64 guid2) +{ + +} + +// done +void Channel::MakeNotifyPacket(WorldPacket *data, uint8 notify_type) +{ + data->Initialize(SMSG_CHANNEL_NOTIFY, 1+m_name.size()+1); + *data << uint8(notify_type); + *data << m_name; +} + +// done 0x00 +void Channel::MakeJoined(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_JOINED_NOTICE); + *data << uint64(guid); +} + +// done 0x01 +void Channel::MakeLeft(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_LEFT_NOTICE); + *data << uint64(guid); +} + +// done 0x02 +void Channel::MakeYouJoined(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_YOU_JOINED_NOTICE); + *data << uint8(GetFlags()); + *data << uint32(GetChannelId()); + *data << uint32(0); +} + +// done 0x03 +void Channel::MakeYouLeft(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_YOU_LEFT_NOTICE); + *data << uint32(GetChannelId()); + *data << uint8(0); // can be 0x00 and 0x01 +} + +// done 0x04 +void Channel::MakeWrongPassword(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_WRONG_PASSWORD_NOTICE); +} + +// done 0x05 +void Channel::MakeNotMember(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_MEMBER_NOTICE); +} + +// done 0x06 +void Channel::MakeNotModerator(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_MODERATOR_NOTICE); +} + +// done 0x07 +void Channel::MakePasswordChanged(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_PASSWORD_CHANGED_NOTICE); + *data << uint64(guid); +} + +// done 0x08 +void Channel::MakeOwnerChanged(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_OWNER_CHANGED_NOTICE); + *data << uint64(guid); +} + +// done 0x09 +void Channel::MakePlayerNotFound(WorldPacket *data, std::string name) +{ + MakeNotifyPacket(data, CHAT_PLAYER_NOT_FOUND_NOTICE); + *data << name; +} + +// done 0x0A +void Channel::MakeNotOwner(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_OWNER_NOTICE); +} + +// done 0x0B +void Channel::MakeChannelOwner(WorldPacket *data) +{ + std::string name = ""; + + if(!objmgr.GetPlayerNameByGUID(m_ownerGUID, name) || name.empty()) + name = "PLAYER_NOT_FOUND"; + + MakeNotifyPacket(data, CHAT_CHANNEL_OWNER_NOTICE); + *data << ((IsConstant() || !m_ownerGUID) ? "Nobody" : name); +} + +// done 0x0C +void Channel::MakeModeChange(WorldPacket *data, uint64 guid, uint8 oldflags) +{ + MakeNotifyPacket(data, CHAT_MODE_CHANGE_NOTICE); + *data << uint64(guid); + *data << uint8(oldflags); + *data << uint8(GetPlayerFlags(guid)); +} + +// done 0x0D +void Channel::MakeAnnouncementsOn(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_ON_NOTICE); + *data << uint64(guid); +} + +// done 0x0E +void Channel::MakeAnnouncementsOff(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_OFF_NOTICE); + *data << uint64(guid); +} + +// done 0x0F +void Channel::MakeModerationOn(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_MODERATION_ON_NOTICE); + *data << uint64(guid); +} + +// done 0x10 +void Channel::MakeModerationOff(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_MODERATION_OFF_NOTICE); + *data << uint64(guid); +} + +// done 0x11 +void Channel::MakeMuted(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_MUTED_NOTICE); +} + +// done 0x12 +void Channel::MakePlayerKicked(WorldPacket *data, uint64 bad, uint64 good) +{ + MakeNotifyPacket(data, CHAT_PLAYER_KICKED_NOTICE); + *data << uint64(bad); + *data << uint64(good); +} + +// done 0x13 +void Channel::MakeBanned(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_BANNED_NOTICE); +} + +// done 0x14 +void Channel::MakePlayerBanned(WorldPacket *data, uint64 bad, uint64 good) +{ + MakeNotifyPacket(data, CHAT_PLAYER_BANNED_NOTICE); + *data << uint64(bad); + *data << uint64(good); +} + +// done 0x15 +void Channel::MakePlayerUnbanned(WorldPacket *data, uint64 bad, uint64 good) +{ + MakeNotifyPacket(data, CHAT_PLAYER_UNBANNED_NOTICE); + *data << uint64(bad); + *data << uint64(good); +} + +// done 0x16 +void Channel::MakePlayerNotBanned(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_PLAYER_NOT_BANNED_NOTICE); + *data << uint64(guid); +} + +// done 0x17 +void Channel::MakePlayerAlreadyMember(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_PLAYER_ALREADY_MEMBER_NOTICE); + *data << uint64(guid); +} + +// done 0x18 +void Channel::MakeInvite(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_INVITE_NOTICE); + *data << uint64(guid); +} + +// done 0x19 +void Channel::MakeInviteWrongFaction(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_INVITE_WRONG_FACTION_NOTICE); +} + +// done 0x1A +void Channel::MakeWrongFaction(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_WRONG_FACTION_NOTICE); +} + +// done 0x1B +void Channel::MakeInvalidName(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_INVALID_NAME_NOTICE); +} + +// done 0x1C +void Channel::MakeNotModerated(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_MODERATED_NOTICE); +} + +// done 0x1D +void Channel::MakePlayerInvited(WorldPacket *data, uint64 guid) +{ + std::string name; + + if(!objmgr.GetPlayerNameByGUID(guid, name) || name.empty()) + return; // player name not found + + MakeNotifyPacket(data, CHAT_PLAYER_INVITED_NOTICE); + *data << name; +} + +// done 0x1E +void Channel::MakePlayerInviteBanned(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_PLAYER_INVITE_BANNED_NOTICE); + *data << uint64(guid); +} + +// done 0x1F +void Channel::MakeThrottled(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_THROTTLED_NOTICE); +} + +// done 0x20 +void Channel::MakeNotInArea(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_IN_AREA_NOTICE); +} + +// done 0x21 +void Channel::MakeNotInLfg(WorldPacket *data) +{ + MakeNotifyPacket(data, CHAT_NOT_IN_LFG_NOTICE); +} + +// done 0x22 +void Channel::MakeVoiceOn(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_VOICE_ON_NOTICE); + *data << uint64(guid); +} + +// done 0x23 +void Channel::MakeVoiceOff(WorldPacket *data, uint64 guid) +{ + MakeNotifyPacket(data, CHAT_VOICE_OFF_NOTICE); + *data << uint64(guid); +} + +void Channel::JoinNotify(uint64 guid) +{ + WorldPacket data; + + if(IsConstant()) + data.Initialize(SMSG_USERLIST_ADD, 8+1+1+4+GetName().size()+1); + else + data.Initialize(SMSG_USERLIST_UPDATE, 8+1+1+4+GetName().size()+1); + + data << uint64(guid); + data << uint8(GetPlayerFlags(guid)); + data << uint8(GetFlags()); + data << uint32(GetNumPlayers()); + data << GetName(); + SendToAll(&data); +} + +void Channel::LeaveNotify(uint64 guid) +{ + WorldPacket data(SMSG_USERLIST_REMOVE, 8+1+4+GetName().size()+1); + data << uint64(guid); + data << uint8(GetFlags()); + data << uint32(GetNumPlayers()); + data << GetName(); + SendToAll(&data); +} diff --git a/src/game/Channel.h b/src/game/Channel.h new file mode 100644 index 00000000000..342992411b6 --- /dev/null +++ b/src/game/Channel.h @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CHANNEL_H +#define _CHANNEL_H + +#include "Common.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Opcodes.h" +#include "Player.h" + +#include +#include +#include + +class Channel +{ + enum ChatNotify + { + CHAT_JOINED_NOTICE = 0x00, //+ "%s joined channel."; + CHAT_LEFT_NOTICE = 0x01, //+ "%s left channel."; + //CHAT_SUSPENDED_NOTICE = 0x01, // "%s left channel."; + CHAT_YOU_JOINED_NOTICE = 0x02, //+ "Joined Channel: [%s]"; -- You joined + //CHAT_YOU_CHANGED_NOTICE = 0x02, // "Changed Channel: [%s]"; + CHAT_YOU_LEFT_NOTICE = 0x03, //+ "Left Channel: [%s]"; -- You left + CHAT_WRONG_PASSWORD_NOTICE = 0x04, //+ "Wrong password for %s."; + CHAT_NOT_MEMBER_NOTICE = 0x05, //+ "Not on channel %s."; + CHAT_NOT_MODERATOR_NOTICE = 0x06, //+ "Not a moderator of %s."; + CHAT_PASSWORD_CHANGED_NOTICE = 0x07, //+ "[%s] Password changed by %s."; + CHAT_OWNER_CHANGED_NOTICE = 0x08, //+ "[%s] Owner changed to %s."; + CHAT_PLAYER_NOT_FOUND_NOTICE = 0x09, //+ "[%s] Player %s was not found."; + CHAT_NOT_OWNER_NOTICE = 0x0A, //+ "[%s] You are not the channel owner."; + CHAT_CHANNEL_OWNER_NOTICE = 0x0B, //+ "[%s] Channel owner is %s."; + CHAT_MODE_CHANGE_NOTICE = 0x0C, //? + CHAT_ANNOUNCEMENTS_ON_NOTICE = 0x0D, //+ "[%s] Channel announcements enabled by %s."; + CHAT_ANNOUNCEMENTS_OFF_NOTICE = 0x0E, //+ "[%s] Channel announcements disabled by %s."; + CHAT_MODERATION_ON_NOTICE = 0x0F, //+ "[%s] Channel moderation enabled by %s."; + CHAT_MODERATION_OFF_NOTICE = 0x10, //+ "[%s] Channel moderation disabled by %s."; + CHAT_MUTED_NOTICE = 0x11, //+ "[%s] You do not have permission to speak."; + CHAT_PLAYER_KICKED_NOTICE = 0x12, //? "[%s] Player %s kicked by %s."; + CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are banned from that channel."; + CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s banned by %s."; + CHAT_PLAYER_UNBANNED_NOTICE = 0x15, //? "[%s] Player %s unbanned by %s."; + CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not banned."; + CHAT_PLAYER_ALREADY_MEMBER_NOTICE = 0x17, //+ "[%s] Player %s is already on the channel."; + CHAT_INVITE_NOTICE = 0x18, //+ "%2$s has invited you to join the channel '%1$s'."; + CHAT_INVITE_WRONG_FACTION_NOTICE = 0x19, //+ "Target is in the wrong alliance for %s."; + CHAT_WRONG_FACTION_NOTICE = 0x1A, //+ "Wrong alliance for %s."; + CHAT_INVALID_NAME_NOTICE = 0x1B, //+ "Invalid channel name"; + CHAT_NOT_MODERATED_NOTICE = 0x1C, //+ "%s is not moderated"; + CHAT_PLAYER_INVITED_NOTICE = 0x1D, //+ "[%s] You invited %s to join the channel"; + CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been banned."; + CHAT_THROTTLED_NOTICE = 0x1F, //+ "[%s] The number of messages that can be sent to this channel is limited, please wait to send another message."; + CHAT_NOT_IN_AREA_NOTICE = 0x20, //+ "[%s] You are not in the correct area for this channel."; -- The user is trying to send a chat to a zone specific channel, and they're not physically in that zone. + CHAT_NOT_IN_LFG_NOTICE = 0x21, //+ "[%s] You must be queued in looking for group before joining this channel."; -- The user must be in the looking for group system to join LFG chat channels. + CHAT_VOICE_ON_NOTICE = 0x22, //+ "[%s] Channel voice enabled by %s."; + CHAT_VOICE_OFF_NOTICE = 0x23, //+ "[%s] Channel voice disabled by %s."; + }; + + enum ChannelFlags + { + CHANNEL_FLAG_NONE = 0x00, + CHANNEL_FLAG_CUSTOM = 0x01, + // 0x02 + CHANNEL_FLAG_TRADE = 0x04, + CHANNEL_FLAG_NOT_LFG = 0x08, + CHANNEL_FLAG_GENERAL = 0x10, + CHANNEL_FLAG_CITY = 0x20, + CHANNEL_FLAG_LFG = 0x40, + CHANNEL_FLAG_VOICE = 0x80 + // General 0x18 = 0x10 | 0x08 + // Trade 0x3C = 0x20 | 0x10 | 0x08 | 0x04 + // LocalDefence 0x18 = 0x10 | 0x08 + // GuildRecruitment 0x38 = 0x20 | 0x10 | 0x08 + // LookingForGroup 0x50 = 0x40 | 0x10 + }; + + enum ChannelDBCFlags + { + CHANNEL_DBC_FLAG_NONE = 0x00000, + CHANNEL_DBC_FLAG_INITIAL = 0x00001, // General, Trade, LocalDefense, LFG + CHANNEL_DBC_FLAG_ZONE_DEP = 0x00002, // General, Trade, LocalDefense, GuildRecruitment + CHANNEL_DBC_FLAG_GLOBAL = 0x00004, // WorldDefense + CHANNEL_DBC_FLAG_TRADE = 0x00008, // Trade + CHANNEL_DBC_FLAG_CITY_ONLY = 0x00010, // Trade, GuildRecruitment + CHANNEL_DBC_FLAG_CITY_ONLY2 = 0x00020, // Trade, GuildRecruitment + CHANNEL_DBC_FLAG_DEFENSE = 0x10000, // LocalDefense, WorldDefense + CHANNEL_DBC_FLAG_GUILD_REQ = 0x20000, // GuildRecruitment + CHANNEL_DBC_FLAG_LFG = 0x40000 // LookingForGroup + }; + + enum ChannelMemberFlags + { + MEMBER_FLAG_NONE = 0x00, + MEMBER_FLAG_OWNER = 0x01, + MEMBER_FLAG_MODERATOR = 0x02, + MEMBER_FLAG_VOICED = 0x04, + MEMBER_FLAG_MUTED = 0x08, + MEMBER_FLAG_CUSTOM = 0x10, + MEMBER_FLAG_MIC_MUTED = 0x20, + // 0x40 + // 0x80 + }; + + struct PlayerInfo + { + uint64 player; + uint8 flags; + + bool HasFlag(uint8 flag) { return flags & flag; } + void SetFlag(uint8 flag) { if(!HasFlag(flag)) flags |= flag; } + bool IsOwner() { return flags & MEMBER_FLAG_OWNER; } + void SetOwner(bool state) + { + if(state) flags |= MEMBER_FLAG_OWNER; + else flags &= ~MEMBER_FLAG_OWNER; + } + bool IsModerator() { return flags & MEMBER_FLAG_MODERATOR; } + void SetModerator(bool state) + { + if(state) flags |= MEMBER_FLAG_MODERATOR; + else flags &= ~MEMBER_FLAG_MODERATOR; + } + bool IsMuted() { return flags & MEMBER_FLAG_MUTED; } + void SetMuted(bool state) + { + if(state) flags |= MEMBER_FLAG_MUTED; + else flags &= ~MEMBER_FLAG_MUTED; + } + }; + + typedef std::map PlayerList; + PlayerList players; + typedef std::list BannedList; + BannedList banned; + bool m_announce; + bool m_moderate; + std::string m_name; + std::string m_password; + uint8 m_flags; + uint32 m_channelId; + uint64 m_ownerGUID; + + private: + // initial packet data (notify type and channel name) + void MakeNotifyPacket(WorldPacket *data, uint8 notify_type); + // type specific packet data + void MakeJoined(WorldPacket *data, uint64 guid); //+ 0x00 + void MakeLeft(WorldPacket *data, uint64 guid); //+ 0x01 + void MakeYouJoined(WorldPacket *data); //+ 0x02 + void MakeYouLeft(WorldPacket *data); //+ 0x03 + void MakeWrongPassword(WorldPacket *data); //? 0x04 + void MakeNotMember(WorldPacket *data); //? 0x05 + void MakeNotModerator(WorldPacket *data); //? 0x06 + void MakePasswordChanged(WorldPacket *data, uint64 guid); //+ 0x07 + void MakeOwnerChanged(WorldPacket *data, uint64 guid); //? 0x08 + void MakePlayerNotFound(WorldPacket *data, std::string name); //+ 0x09 + void MakeNotOwner(WorldPacket *data); //? 0x0A + void MakeChannelOwner(WorldPacket *data); //? 0x0B + void MakeModeChange(WorldPacket *data, uint64 guid, uint8 oldflags); //+ 0x0C + void MakeAnnouncementsOn(WorldPacket *data, uint64 guid); //+ 0x0D + void MakeAnnouncementsOff(WorldPacket *data, uint64 guid); //+ 0x0E + void MakeModerationOn(WorldPacket *data, uint64 guid); //+ 0x0F + void MakeModerationOff(WorldPacket *data, uint64 guid); //+ 0x10 + void MakeMuted(WorldPacket *data); //? 0x11 + void MakePlayerKicked(WorldPacket *data, uint64 bad, uint64 good); //? 0x12 + void MakeBanned(WorldPacket *data); //? 0x13 + void MakePlayerBanned(WorldPacket *data, uint64 bad, uint64 good); //? 0x14 + void MakePlayerUnbanned(WorldPacket *data, uint64 bad, uint64 good); //? 0x15 + void MakePlayerNotBanned(WorldPacket *data, uint64 guid); //? 0x16 + void MakePlayerAlreadyMember(WorldPacket *data, uint64 guid); //+ 0x17 + void MakeInvite(WorldPacket *data, uint64 guid); //? 0x18 + void MakeInviteWrongFaction(WorldPacket *data); //? 0x19 + void MakeWrongFaction(WorldPacket *data); //? 0x1A + void MakeInvalidName(WorldPacket *data); //? 0x1B + void MakeNotModerated(WorldPacket *data); //? 0x1C + void MakePlayerInvited(WorldPacket *data, uint64 guid); //+ 0x1D + void MakePlayerInviteBanned(WorldPacket *data, uint64 guid); //? 0x1E + void MakeThrottled(WorldPacket *data); //? 0x1F + void MakeNotInArea(WorldPacket *data); //? 0x20 + void MakeNotInLfg(WorldPacket *data); //? 0x21 + void MakeVoiceOn(WorldPacket *data, uint64 guid); //+ 0x22 + void MakeVoiceOff(WorldPacket *data, uint64 guid); //+ 0x23 + + void SendToAll(WorldPacket *data, uint64 p = 0); + void SendToAllButOne(WorldPacket *data, uint64 who); + void SendToOne(WorldPacket *data, uint64 who); + + bool IsOn(uint64 who) const { return players.count(who) > 0; } + + bool IsBanned(const uint64 guid) const + { + for(BannedList::const_iterator i = banned.begin(); i != banned.end(); ++i) + if(*i == guid) + return true; + return false; + } + + bool IsFirst() const { return !(players.size() > 1); } + + uint8 GetPlayerFlags(uint64 p) const + { + PlayerList::const_iterator p_itr = players.find(p); + if(p_itr == players.end()) + return 0; + + return p_itr->second.flags; + } + + void SetModerator(uint64 p, bool set) + { + if(players[p].IsModerator() != set) + { + uint8 oldFlag = GetPlayerFlags(p); + players[p].SetModerator(set); + + WorldPacket data; + MakeModeChange(&data, p, oldFlag); + SendToAll(&data); + } + } + + void SetMute(uint64 p, bool set) + { + if(players[p].IsMuted() != set) + { + uint8 oldFlag = GetPlayerFlags(p); + players[p].SetMuted(set); + + WorldPacket data; + MakeModeChange(&data, p, oldFlag); + SendToAll(&data); + } + } + + public: + Channel(std::string name, uint32 channel_id); + std::string GetName() const { return m_name; } + uint32 GetChannelId() const { return m_channelId; } + bool IsConstant() const { return m_channelId != 0; } + bool IsAnnounce() const { return m_announce; } + bool IsLFG() const { return GetFlags() & CHANNEL_FLAG_LFG; } + std::string GetPassword() const { return m_password; } + void SetPassword(std::string npassword) { m_password = npassword; } + void SetAnnounce(bool nannounce) { m_announce = nannounce; } + uint32 GetNumPlayers() const { return players.size(); } + uint8 GetFlags() const { return m_flags; } + bool HasFlag(uint8 flag) { return m_flags & flag; } + + void Join(uint64 p, const char *pass); + void Leave(uint64 p, bool send = true); + void KickOrBan(uint64 good, const char *badname, bool ban); + void Kick(uint64 good, const char *badname) { KickOrBan(good, badname, false); } + void Ban(uint64 good, const char *badname) { KickOrBan(good, badname, true); } + void UnBan(uint64 good, const char *badname); + void Password(uint64 p, const char *pass); + void SetMode(uint64 p, const char *p2n, bool mod, bool set); + void SetOwner(uint64 p, bool exclaim = true); + void SetOwner(uint64 p, const char *newname); + void SendWhoOwner(uint64 p); + void SetModerator(uint64 p, const char *newname) { SetMode(p, newname, true, true); } + void UnsetModerator(uint64 p, const char *newname) { SetMode(p, newname, true, false); } + void SetMute(uint64 p, const char *newname) { SetMode(p, newname, false, true); } + void UnsetMute(uint64 p, const char *newname) { SetMode(p, newname, false, false); } + void List(Player* p); + void Announce(uint64 p); + void Moderate(uint64 p); + void Say(uint64 p, const char *what, uint32 lang); + void Invite(uint64 p, const char *newp); + void Voice(uint64 guid1, uint64 guid2); + void DeVoice(uint64 guid1, uint64 guid2); + void JoinNotify(uint64 guid); // invisible notify + void LeaveNotify(uint64 guid); // invisible notify +}; +#endif diff --git a/src/game/ChannelHandler.cpp b/src/game/ChannelHandler.cpp new file mode 100644 index 00000000000..4c8bdd1c516 --- /dev/null +++ b/src/game/ChannelHandler.cpp @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ObjectMgr.h" // for normalizePlayerName +#include "ChannelMgr.h" +#include "Policies/SingletonImp.h" + +INSTANTIATE_SINGLETON_1( AllianceChannelMgr ); +INSTANTIATE_SINGLETON_1( HordeChannelMgr ); + +void WorldSession::HandleChannelJoin(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 4+1+1+1); + + uint32 channel_id; + uint8 unknown1, unknown2; + std::string channelname, pass; + + recvPacket >> channel_id >> unknown1 >> unknown2; + recvPacket >> channelname; + + if(channelname.empty()) + return; + + // recheck + CHECK_PACKET_SIZE(recvPacket, 4+1+1+(channelname.size()+1)+1); + + recvPacket >> pass; + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetJoinChannel(channelname, channel_id)) + chn->Join(_player->GetGUID(), pass.c_str()); +} + +void WorldSession::HandleChannelLeave(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 4+1); + + uint32 unk; + std::string channelname; + recvPacket >> unk; // channel id? + recvPacket >> channelname; + + if(channelname.empty()) + return; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + { + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Leave(_player->GetGUID(), true); + cMgr->LeftChannel(channelname); + } +} + +void WorldSession::HandleChannelList(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string channelname; + recvPacket >> channelname; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->List(_player); +} + +void WorldSession::HandleChannelPassword(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1+1); + + std::string channelname, pass; + recvPacket >> channelname; + + // recheck + CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1); + + recvPacket >> pass; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Password(_player->GetGUID(), pass.c_str()); +} + +void WorldSession::HandleChannelSetOwner(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1+1); + + std::string channelname, newp; + recvPacket >> channelname; + + // recheck + CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1); + + recvPacket >> newp; + + if(!normalizePlayerName(newp)) + return; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->SetOwner(_player->GetGUID(), newp.c_str()); +} + +void WorldSession::HandleChannelOwner(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string channelname; + recvPacket >> channelname; + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->SendWhoOwner(_player->GetGUID()); +} + +void WorldSession::HandleChannelModerator(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1+1); + + std::string channelname, otp; + recvPacket >> channelname; + + // recheck + CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1); + + recvPacket >> otp; + + if(!normalizePlayerName(otp)) + return; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->SetModerator(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelUnmoderator(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1+1); + + std::string channelname, otp; + recvPacket >> channelname; + + // recheck + CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1); + + recvPacket >> otp; + + if(!normalizePlayerName(otp)) + return; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->UnsetModerator(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelMute(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1+1); + + std::string channelname, otp; + recvPacket >> channelname; + + // recheck + CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1); + + recvPacket >> otp; + + if(!normalizePlayerName(otp)) + return; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->SetMute(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelUnmute(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1+1); + + std::string channelname, otp; + recvPacket >> channelname; + + // recheck + CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1); + + recvPacket >> otp; + + if(!normalizePlayerName(otp)) + return; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->UnsetMute(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelInvite(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1+1); + + std::string channelname, otp; + recvPacket >> channelname; + + // recheck + CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1); + + recvPacket >> otp; + + if(!normalizePlayerName(otp)) + return; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Invite(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelKick(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1+1); + + std::string channelname, otp; + recvPacket >> channelname; + + // recheck + CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1); + + recvPacket >> otp; + if(!normalizePlayerName(otp)) + return; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Kick(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelBan(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1+1); + + std::string channelname, otp; + recvPacket >> channelname; + + // recheck + CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1); + + recvPacket >> otp; + + if(!normalizePlayerName(otp)) + return; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Ban(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelUnban(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1+1); + + std::string channelname, otp; + recvPacket >> channelname; + + // recheck + CHECK_PACKET_SIZE(recvPacket, (channelname.size()+1)+1); + + recvPacket >> otp; + + if(!normalizePlayerName(otp)) + return; + + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->UnBan(_player->GetGUID(), otp.c_str()); +} + +void WorldSession::HandleChannelAnnounce(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string channelname; + recvPacket >> channelname; + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Announce(_player->GetGUID()); +} + +void WorldSession::HandleChannelModerate(WorldPacket& recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string channelname; + recvPacket >> channelname; + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->Moderate(_player->GetGUID()); +} + +void WorldSession::HandleChannelRosterQuery(WorldPacket &recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string channelname; + recvPacket >> channelname; + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->List(_player); +} + +void WorldSession::HandleChannelInfoQuery(WorldPacket &recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string channelname; + recvPacket >> channelname; + if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + { + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + { + WorldPacket data(SMSG_CHANNEL_MEMBER_COUNT, chn->GetName().size()+1+1+4); + data << chn->GetName(); + data << uint8(chn->GetFlags()); + data << uint32(chn->GetNumPlayers()); + SendPacket(&data); + } + } +} + +void WorldSession::HandleChannelJoinNotify(WorldPacket &recvPacket) +{ + sLog.outDebug("Opcode %u", recvPacket.GetOpcode()); + //recvPacket.hexlike(); + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string channelname; + recvPacket >> channelname; + /*if(ChannelMgr* cMgr = channelMgr(_player->GetTeam())) + if(Channel *chn = cMgr->GetChannel(channelname, _player)) + chn->JoinNotify(_player->GetGUID());*/ +} diff --git a/src/game/ChannelMgr.h b/src/game/ChannelMgr.h new file mode 100644 index 00000000000..eb15f08a39e --- /dev/null +++ b/src/game/ChannelMgr.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef MANGOSSERVER_CHANNELMGR_H +#define MANGOSSERVER_CHANNELMGR_H + +#include "Channel.h" +#include "Policies/Singleton.h" +#include "World.h" + +#include +#include + +class ChannelMgr +{ + public: + typedef std::map ChannelMap; + ChannelMgr() {} + ~ChannelMgr() + { + for(ChannelMap::iterator itr = channels.begin();itr!=channels.end(); ++itr) + delete itr->second; + channels.clear(); + } + Channel *GetJoinChannel(std::string name, uint32 channel_id) + { + if(channels.count(name) == 0) + { + Channel *nchan = new Channel(name,channel_id); + channels[name] = nchan; + } + return channels[name]; + } + Channel *GetChannel(std::string name, Player *p) + { + ChannelMap::const_iterator i = channels.find(name); + + if(i == channels.end()) + { + WorldPacket data; + MakeNotOnPacket(&data,name); + p->GetSession()->SendPacket(&data); + return NULL; + } + else + return i->second; + } + void LeftChannel(std::string name) + { + ChannelMap::const_iterator i = channels.find(name); + + if(i == channels.end()) + return; + + Channel* channel = i->second; + + if(channel->GetNumPlayers() == 0 && !channel->IsConstant()) + { + channels.erase(name); + delete channel; + } + } + private: + ChannelMap channels; + void MakeNotOnPacket(WorldPacket *data, std::string name) + { + data->Initialize(SMSG_CHANNEL_NOTIFY, (1+10)); // we guess size + (*data) << (uint8)0x05 << name; + } +}; + +class AllianceChannelMgr : public ChannelMgr {}; +class HordeChannelMgr : public ChannelMgr {}; + +inline ChannelMgr* channelMgr(uint32 team) +{ + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)) + //For Test,No Seprate Faction + return &MaNGOS::Singleton::Instance(); + + if(team==ALLIANCE) + return &MaNGOS::Singleton::Instance(); + if(team==HORDE) + return &MaNGOS::Singleton::Instance(); + return NULL; +} +#endif diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp new file mode 100644 index 00000000000..f63af7d7f81 --- /dev/null +++ b/src/game/CharacterHandler.cpp @@ -0,0 +1,1065 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "WorldPacket.h" +#include "WorldSocket.h" +#include "WorldSession.h" +#include "Opcodes.h" +#include "Log.h" +#include "World.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "Guild.h" +#include "UpdateMask.h" +#include "Auth/md5.h" +#include "MapManager.h" +#include "ObjectAccessor.h" +#include "Group.h" +#include "Database/DatabaseImpl.h" +#include "PlayerDump.h" +#include "SocialMgr.h" +#include "Util.h" + +class LoginQueryHolder : public SqlQueryHolder +{ + private: + uint32 m_accountId; + uint64 m_guid; + public: + LoginQueryHolder(uint32 accountId, uint64 guid) + : m_accountId(accountId), m_guid(guid) { } + uint64 GetGuid() const { return m_guid; } + uint32 GetAccountId() const { return m_accountId; } + bool Initialize(); +}; + +bool LoginQueryHolder::Initialize() +{ + SetSize(MAX_PLAYER_LOGIN_QUERY); + + bool res = true; + + // NOTE: all fields in `characters` must be read to prevent lost character data at next save in case wrong DB structure. + // !!! NOTE: including unused `zone`,`online` + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, data, name, race, class, position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, gmstate, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT leaderGuid FROM group_member WHERE memberGuid ='%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADBOUNDINSTANCES, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADAURAS, "SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLS, "SELECT spell,slot,active,disabled FROM character_spell WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS, "SELECT quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4 FROM character_queststatus WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS,"SELECT quest,time FROM character_queststatus_daily WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTUTORIALS, "SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u' AND realmid = '%u'", GetAccountId(), realmID); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADREPUTATION, "SELECT faction,standing,flags FROM character_reputation WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, "SELECT data,bag,slot,item,item_template FROM character_inventory JOIN item_instance ON character_inventory.item = item_instance.guid WHERE character_inventory.guid = '%u' ORDER BY bag,slot", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, "SELECT button,action,type,misc FROM character_action WHERE guid = '%u' ORDER BY button", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" I64FMTD "'", GUID_LOPART(m_guid),(uint64)time(NULL)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADMAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSOCIALLIST, "SELECT friend,flags,note FROM character_social WHERE guid = '%u' LIMIT 255", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADHOMEBIND, "SELECT map,zone,position_x,position_y,position_z FROM character_homebind WHERE guid = '%u'", GUID_LOPART(m_guid)); + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADSPELLCOOLDOWNS, "SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'", GUID_LOPART(m_guid)); + if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED)) + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = '%u'",GUID_LOPART(m_guid)); + // in other case still be dummy query + res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGUILD, "SELECT guildid,rank FROM guild_member WHERE guid = '%u'", GUID_LOPART(m_guid)); + + return res; +} + +// don't call WorldSession directly +// it may get deleted before the query callbacks get executed +// instead pass an account id to this handler +class CharacterHandler +{ + public: + void HandleCharEnumCallback(QueryResult * result, uint32 account) + { + WorldSession * session = sWorld.FindSession(account); + if(!session) + { + delete result; + return; + } + session->HandleCharEnum(result); + } + void HandlePlayerLoginCallback(QueryResult * /*dummy*/, SqlQueryHolder * holder) + { + if (!holder) return; + WorldSession *session = sWorld.FindSession(((LoginQueryHolder*)holder)->GetAccountId()); + if(!session) + { + delete holder; + return; + } + session->HandlePlayerLogin((LoginQueryHolder*)holder); + } +} chrHandler; + +void WorldSession::HandleCharEnum(QueryResult * result) +{ + // keys can be non cleared if player open realm list and close it by 'cancel' + loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE id = '%u'", GetAccountId()); + + WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size + + uint8 num = 0; + + data << num; + + if( result ) + { + Player *plr = new Player(this); + do + { + sLog.outDetail("Loading char guid %u from account %u.",(*result)[0].GetUInt32(),GetAccountId()); + + if(plr->MinimalLoadFromDB( result, (*result)[0].GetUInt32() )) + { + plr->BuildEnumData( result, &data ); + ++num; + } + } + while( result->NextRow() ); + + delete plr; + delete result; + } + + data.put(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 = _socket ? _socket->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 = _socket ? _socket->GetRemoteAddress().c_str() : "-"; + 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 (posGetGuildId() != 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<GetName(); + data<GetGUID(); + guild->BroadcastPacket(&data); + DEBUG_LOG( "WORLD: Sent guild-signed-on (SMSG_GUILD_EVENT)" ); + + // Increment online members of the guild + guild->IncOnlineMemberCount(); + } + else + { + // remove wrong guild data + sLog.outError("Player %s (GUID: %u) marked as member not existed guild (id: %u), removing guild membership for player.",pCurrChar->GetName(),pCurrChar->GetGUIDLow(),pCurrChar->GetGuildId()); + pCurrChar->SetUInt32Value(PLAYER_GUILDID,0); + pCurrChar->SetUInt32ValueInDB(PLAYER_GUILDID,0,pCurrChar->GetGUID()); + } + } + + if(!pCurrChar->isAlive()) + pCurrChar->SendCorpseReclaimDelay(true); + + pCurrChar->SendInitialPacketsBeforeAddToMap(); + + //Show cinematic at the first time that player login + if( !pCurrChar->getCinematic() ) + { + pCurrChar->setCinematic(1); + + ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace()); + if(rEntry) + { + data.Initialize( SMSG_TRIGGER_CINEMATIC,4 ); + data << uint32(rEntry->startmovie); + SendPacket( &data ); + } + } + + //QueryResult *result = CharacterDatabase.PQuery("SELECT guildid,rank FROM guild_member WHERE guid = '%u'",pCurrChar->GetGUIDLow()); + QueryResult *resultGuild = holder->GetResult(PLAYER_LOGIN_QUERY_LOADGUILD); + + if(resultGuild) + { + Field *fields = resultGuild->Fetch(); + pCurrChar->SetInGuild(fields[0].GetUInt32()); + pCurrChar->SetRank(fields[1].GetUInt32()); + delete resultGuild; + } + else if(pCurrChar->GetGuildId()) // clear guild related fields in case wrong data about non existed membership + { + pCurrChar->SetInGuild(0); + pCurrChar->SetRank(0); + } + + if (!MapManager::Instance().GetMap(pCurrChar->GetMapId(), pCurrChar)->Add(pCurrChar)) + { + AreaTrigger const* at = objmgr.GetGoBackTrigger(pCurrChar->GetMapId()); + if(at) + pCurrChar->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, pCurrChar->GetOrientation()); + else + pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation()); + } + + ObjectAccessor::Instance().AddObject(pCurrChar); + //sLog.outDebug("Player %s added to Map.",pCurrChar->GetName()); + pCurrChar->GetSocial()->SendSocialList(); + + pCurrChar->SendInitialPacketsAfterAddToMap(); + + CharacterDatabase.PExecute("UPDATE characters SET online = 1 WHERE guid = '%u'", pCurrChar->GetGUIDLow()); + loginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = '%u'", GetAccountId()); + pCurrChar->SetInGameTime( getMSTime() ); + + // announce group about member online (must be after add to player list to receive announce to self) + if(Group *group = pCurrChar->GetGroup()) + { + //pCurrChar->groupInfo.group->SendInit(this); // useless + group->SendUpdate(); + } + + // friend status + sSocialMgr.SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUIDLow(), "", true); + + // Place character in world (and load zone) before some object loading + pCurrChar->LoadCorpse(); + + // setting Ghost+speed if dead + //if ( pCurrChar->m_deathState == DEAD ) + if (pCurrChar->m_deathState != ALIVE) + { + // not blizz like, we must correctly save and load player instead... + if(pCurrChar->getRace() == RACE_NIGHTELF) + pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form) + pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) + + //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+41, 8326); + //pCurrChar->SetUInt32Value(UNIT_FIELD_AURA+42, 20584); + //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAFLAGS+6, 238); + //pCurrChar->SetUInt32Value(UNIT_FIELD_AURALEVELS+11, 514); + //pCurrChar->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS+11, 65535); + //pCurrChar->SetUInt32Value(UNIT_FIELD_DISPLAYID, 1825); + //if (pCurrChar->getRace() == RACE_NIGHTELF) + //{ + // pCurrChar->SetSpeed(MOVE_RUN, 1.5f*1.2f, true); + // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f*1.2f, true); + //} + //else + //{ + // pCurrChar->SetSpeed(MOVE_RUN, 1.5f, true); + // pCurrChar->SetSpeed(MOVE_SWIM, 1.5f, true); + //} + pCurrChar->SetMovement(MOVE_WATER_WALK); + } + + if(uint32 sourceNode = pCurrChar->m_taxi.GetTaxiSource()) + { + + sLog.outDebug( "WORLD: Restart character %u taxi flight", pCurrChar->GetGUIDLow() ); + + uint32 MountId = objmgr.GetTaxiMount(sourceNode, pCurrChar->GetTeam()); + uint32 path = pCurrChar->m_taxi.GetCurrentTaxiPath(); + + // search appropriate start path node + uint32 startNode = 0; + + TaxiPathNodeList const& nodeList = sTaxiPathNodesByPath[path]; + + float distPrev = MAP_SIZE*MAP_SIZE; + float distNext = + (nodeList[0].x-pCurrChar->GetPositionX())*(nodeList[0].x-pCurrChar->GetPositionX())+ + (nodeList[0].y-pCurrChar->GetPositionY())*(nodeList[0].y-pCurrChar->GetPositionY())+ + (nodeList[0].z-pCurrChar->GetPositionZ())*(nodeList[0].z-pCurrChar->GetPositionZ()); + + for(uint32 i = 1; i < nodeList.size(); ++i) + { + TaxiPathNode const& node = nodeList[i]; + TaxiPathNode const& prevNode = nodeList[i-1]; + + // skip nodes at another map + if(node.mapid != pCurrChar->GetMapId()) + continue; + + distPrev = distNext; + + distNext = + (node.x-pCurrChar->GetPositionX())*(node.x-pCurrChar->GetPositionX())+ + (node.y-pCurrChar->GetPositionY())*(node.y-pCurrChar->GetPositionY())+ + (node.z-pCurrChar->GetPositionZ())*(node.z-pCurrChar->GetPositionZ()); + + float distNodes = + (node.x-prevNode.x)*(node.x-prevNode.x)+ + (node.y-prevNode.y)*(node.y-prevNode.y)+ + (node.z-prevNode.z)*(node.z-prevNode.z); + + if(distNext + distPrev < distNodes) + { + startNode = i; + break; + } + } + + SendDoFlight( MountId, path, startNode ); + } + + // Load pet if any and player is alive and not in taxi flight + if(pCurrChar->isAlive() && pCurrChar->m_taxi.GetTaxiSource()==0) + pCurrChar->LoadPet(); + + // Set FFA PvP for non GM in non-rest mode + if(sWorld.IsFFAPvPRealm() && !pCurrChar->isGameMaster() && !pCurrChar->HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_RESTING) ) + pCurrChar->SetFlag(PLAYER_FLAGS,PLAYER_FLAGS_FFA_PVP); + + if(pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP)) + pCurrChar->SetContestedPvP(); + + // Apply at_login requests + if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS)) + { + pCurrChar->resetSpells(); + SendNotification("Spells has been reset."); + } + + if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS)) + { + pCurrChar->resetTalents(true); + SendNotification("Talents has been reset."); + } + + // show time before shutdown if shutdown planned. + if(sWorld.IsShutdowning()) + sWorld.ShutdownMsg(true,pCurrChar); + + if(pCurrChar->isGameMaster()) + SendNotification("GM mode is ON"); + + std::string IP_str = _socket ? _socket->GetRemoteAddress().c_str() : "-"; + 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::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 = _socket ? _socket->GetRemoteAddress().c_str() : "-"; + 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 new file mode 100644 index 00000000000..9635542cf4f --- /dev/null +++ b/src/game/Chat.cpp @@ -0,0 +1,1103 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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; + +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; +} + +LanguageDesc const* GetLanguageDescBySpell(uint32 spell_id) +{ + for(int i = 0; i < LANGUAGES_COUNT; ++i) + { + if(lang_description[i].spell_id == spell_id) + return &lang_description[i]; + } + + return NULL; +} + +LanguageDesc const* GetLanguageDescBySkill(uint32 skill_id) +{ + for(int i = 0; i < LANGUAGES_COUNT; ++i) + { + if(lang_description[i].skill_id == skill_id) + return &lang_description[i]; + } + + return NULL; +} + +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 }, + { 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_quest", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllQuestCommand, "", NULL }, + { "all_loot", SEC_ADMINISTRATOR, &ChatHandler::HandleReloadAllLootCommand, "", 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 }, + { "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 }, + { "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 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 }, + { "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[] = + { + { "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 }, + + { 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* s1, const char* s2) +{ + for(;;) + { + if( !*s2 ) + return true; + else if( !*s1 ) + return false; + else if( tolower( *s1 ) != tolower( *s2 ) ) + return false; + ++s1; ++s2; + } +} + +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; + + if(!cmd.length()) + return false; + + for(uint32 i = 0; table[i].Name != NULL; i++) + { + // allow pass "" command name in table + if(strlen(table[i].Name) && !hasStringAbbr(table[i].Name, cmd.c_str())) + 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(strlen(table[i].Name) && !hasStringAbbr(table[i].Name, subcmd)) + continue; + + (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(strlen(table[i].Name) && !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 checker(obj,go_check); + + TypeContainerVisitor, GridTypeMapContainer > object_checker(checker); + CellLock 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]; +} + diff --git a/src/game/Chat.h b/src/game/Chat.h new file mode 100644 index 00000000000..49b219e875e --- /dev/null +++ b/src/game/Chat.h @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_CHAT_H +#define MANGOSSERVER_CHAT_H + +#include "SharedDefines.h" + +class ChatHandler; +class WorldSession; +class Creature; +class Player; +class Unit; + +struct LanguageDesc +{ + Language lang_id; + uint32 spell_id; + uint32 skill_id; +}; + +extern LanguageDesc lang_description[LANGUAGES_COUNT]; + +LanguageDesc const* GetLanguageDescByID(uint32 lang); +LanguageDesc const* GetLanguageDescBySpell(uint32 spell_id); +LanguageDesc const* GetLanguageDescBySkill(uint32 skill_id); + +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* s1, const char* s2); + 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 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 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 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 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 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 HandleLookupItemCommand(const char * args); + bool HandleLookupItemSetCommand(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 HandleLookupTeleCommand(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 HandleLookupAreaCommand(const char* args); + bool HandleLookupCreatureCommand(const char* args); + bool HandleLookupEventCommand(const char* args); + bool HandleLookupFactionCommand(const char * args); + bool HandleLookupObjectCommand(const char* args); + bool HandleLookupQuestCommand(const char* args); + bool HandleLookupSkillCommand(const char* args); + bool HandleLookupSpellCommand(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); + + //! 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); + + 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); + + 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); + + 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 new file mode 100644 index 00000000000..dd3a711858d --- /dev/null +++ b/src/game/ChatHandler.cpp @@ -0,0 +1,583 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Log.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "Opcodes.h" +#include "ObjectMgr.h" +#include "Chat.h" +#include "Database/DatabaseEnv.h" +#include "ChannelMgr.h" +#include "Group.h" +#include "Guild.h" +#include "MapManager.h" +#include "ObjectAccessor.h" +#include "ScriptCalls.h" +#include "Player.h" +#include "SpellAuras.h" +#include "Language.h" +#include "Util.h" + +void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+4+1); + + uint32 type; + uint32 lang; + + recv_data >> type; + recv_data >> lang; + + if(type >= MAX_CHAT_MSG_TYPE) + { + sLog.outError("CHAT: Wrong message type received: %u", type); + return; + } + + //sLog.outDebug("CHAT: packet received. type %u, lang %u", type, lang ); + + // prevent talking at unknown language (cheating) + LanguageDesc const* langDesc = GetLanguageDescByID(lang); + if(!langDesc) + { + SendNotification("Unknown language"); + return; + } + if(langDesc->skill_id != 0 && !_player->HasSkill(langDesc->skill_id)) + { + // also check SPELL_AURA_COMPREHEND_LANGUAGE (client offers option to speak in that language) + Unit::AuraList const& langAuras = _player->GetAurasByType(SPELL_AURA_COMPREHEND_LANGUAGE); + bool foundAura = false; + for(Unit::AuraList::const_iterator i = langAuras.begin();i != langAuras.end(); ++i) + { + if((*i)->GetModifier()->m_miscvalue == lang) + { + foundAura = true; + break; + } + } + if(!foundAura) + { + SendNotification("You don't know that language"); + return; + } + } + + if(lang == LANG_ADDON) + { + // Disabled addon channel? + if(!sWorld.getConfig(CONFIG_ADDON_CHANNEL)) + return; + } + // LANG_ADDON should not be changed nor be affected by flood control + else + { + // send in universal language if player in .gmon mode (ignore spell effects) + if (_player->isGameMaster()) + lang = LANG_UNIVERSAL; + else + { + // send in universal language in two side iteration allowed mode + if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT)) + lang = LANG_UNIVERSAL; + else + { + switch(type) + { + case CHAT_MSG_PARTY: + case CHAT_MSG_RAID: + case CHAT_MSG_RAID_LEADER: + case CHAT_MSG_RAID_WARNING: + // allow two side chat at group channel if two side group allowed + if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) + lang = LANG_UNIVERSAL; + break; + case CHAT_MSG_GUILD: + case CHAT_MSG_OFFICER: + // allow two side chat at guild channel if two side guild allowed + if(sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) + lang = LANG_UNIVERSAL; + break; + } + } + + // but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used) + Unit::AuraList const& ModLangAuras = _player->GetAurasByType(SPELL_AURA_MOD_LANGUAGE); + if(!ModLangAuras.empty()) + lang = ModLangAuras.front()->GetModifier()->m_miscvalue; + } + + if (!_player->CanSpeak()) + { + std::string timeStr = secsToTimeString(m_muteTime - time(NULL)); + SendNotification(GetMangosString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str()); + return; + } + + if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND) + GetPlayer()->UpdateSpeakTime(); + } + + switch(type) + { + case CHAT_MSG_SAY: + case CHAT_MSG_EMOTE: + case CHAT_MSG_YELL: + { + std::string msg = ""; + recv_data >> msg; + + if(msg.empty()) + break; + + if (ChatHandler(this).ParseCommands(msg.c_str()) > 0) + break; + + // strip invisible characters for non-addon messages + if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) + stripLineInvisibleChars(msg); + + if(msg.empty()) + break; + + if(type == CHAT_MSG_SAY) + GetPlayer()->Say(msg, lang); + else if(type == CHAT_MSG_EMOTE) + GetPlayer()->TextEmote(msg); + else if(type == CHAT_MSG_YELL) + GetPlayer()->Yell(msg, lang); + } break; + + case CHAT_MSG_WHISPER: + { + std::string to, msg; + recv_data >> to; + CHECK_PACKET_SIZE(recv_data,4+4+(to.size()+1)+1); + recv_data >> msg; + + // strip invisible characters for non-addon messages + if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING)) + stripLineInvisibleChars(msg); + + if(msg.empty()) + break; + + if(!normalizePlayerName(to)) + { + WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1)); + data<GetSession()->GetSecurity() : 0; + if(!player || tSecurity == SEC_PLAYER && pSecurity > SEC_PLAYER && !player->isAcceptWhispers()) + { + WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1)); + data<GetTeam(); + uint32 sideb = player->GetTeam(); + if( sidea != sideb ) + { + WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1)); + data<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(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/CombatHandler.cpp b/src/game/CombatHandler.cpp new file mode 100644 index 00000000000..047c639e44e --- /dev/null +++ b/src/game/CombatHandler.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Log.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "ObjectAccessor.h" +#include "CreatureAI.h" +#include "ObjectDefines.h" + +void WorldSession::HandleAttackSwingOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid; + recv_data >> guid; + + DEBUG_LOG( "WORLD: Recvd CMSG_ATTACKSWING Message guidlow:%u guidhigh:%u", GUID_LOPART(guid), GUID_HIPART(guid) ); + + Unit *pEnemy = ObjectAccessor::GetUnit(*_player, guid); + + if(!pEnemy) + { + if(!IS_UNIT_GUID(guid)) + sLog.outError("WORLD: Object %u (TypeID: %u) isn't player, pet or creature",GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid))); + else + sLog.outError( "WORLD: Enemy %s %u not found",GetLogNameForGuid(guid),GUID_LOPART(guid)); + + // stop attack state at client + SendAttackStop(NULL); + return; + } + + if(_player->IsFriendlyTo(pEnemy) || pEnemy->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE)) + { + sLog.outError( "WORLD: Enemy %s %u is friendly",(IS_PLAYER_GUID(guid) ? "player" : "creature"),GUID_LOPART(guid)); + + // stop attack state at client + SendAttackStop(pEnemy); + return; + } + + if(!pEnemy->isAlive()) + { + // client can generate swing to known dead target if autoswitch between autoshot and autohit is enabled in client options + // stop attack state at client + SendAttackStop(pEnemy); + return; + } + + _player->Attack(pEnemy,true); +} + +void WorldSession::HandleAttackStopOpcode( WorldPacket & /*recv_data*/ ) +{ + GetPlayer()->AttackStop(); +} + +void WorldSession::HandleSetSheathedOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4); + + uint32 sheathed; + recv_data >> sheathed; + + //sLog.outDebug( "WORLD: Recvd CMSG_SETSHEATHED Message guidlow:%u value1:%u", GetPlayer()->GetGUIDLow(), sheathed ); + + GetPlayer()->SetSheath(sheathed); +} + +void WorldSession::SendAttackStop(Unit const* enemy) +{ + WorldPacket data( SMSG_ATTACKSTOP, (4+20) ); // we guess size + data.append(GetPlayer()->GetPackGUID()); + data.append(enemy ? enemy->GetPackGUID() : 0); // must be packed guid + data << uint32(0); // unk, can be 1 also + SendPacket(&data); +} diff --git a/src/game/ConfusedMovementGenerator.cpp b/src/game/ConfusedMovementGenerator.cpp new file mode 100644 index 00000000000..f1e7c2c1548 --- /dev/null +++ b/src/game/ConfusedMovementGenerator.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Creature.h" +#include "MapManager.h" +#include "Opcodes.h" +#include "ConfusedMovementGenerator.h" +#include "DestinationHolderImp.h" + +template +void +ConfusedMovementGenerator::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::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok) +{ + is_water_ok = creature.canSwim(); + is_land_ok = creature.canWalk(); +} + +template<> +void +ConfusedMovementGenerator::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok) +{ + is_water_ok = true; + is_land_ok = true; +} + +template +void +ConfusedMovementGenerator::Reset(T &unit) +{ + i_nextMove = 1; + i_nextMoveTime.Reset(0); + i_destinationHolder.ResetUpdate(); + unit.StopMoving(); +} + +template +bool +ConfusedMovementGenerator::Update(T &unit, const uint32 &diff) +{ + if(!&unit) + return true; + + if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED)) + return true; + + if( i_nextMoveTime.Passed() ) + { + // currently moving, update location + Traveller 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 traveller(unit); + i_destinationHolder.SetDestination(traveller, x, y, z); + } + } + return true; +} + +template +void +ConfusedMovementGenerator::Finalize(T &unit) +{ + unit.clearUnitState(UNIT_STAT_CONFUSED); +} + +template void ConfusedMovementGenerator::Initialize(Player &player); +template void ConfusedMovementGenerator::Initialize(Creature &creature); +template void ConfusedMovementGenerator::Finalize(Player &player); +template void ConfusedMovementGenerator::Finalize(Creature &creature); +template void ConfusedMovementGenerator::Reset(Player &player); +template void ConfusedMovementGenerator::Reset(Creature &creature); +template bool ConfusedMovementGenerator::Update(Player &player, const uint32 &diff); +template bool ConfusedMovementGenerator::Update(Creature &creature, const uint32 &diff); diff --git a/src/game/ConfusedMovementGenerator.h b/src/game/ConfusedMovementGenerator.h new file mode 100644 index 00000000000..78955297395 --- /dev/null +++ b/src/game/ConfusedMovementGenerator.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_RANDOMMOTIONGENERATOR_H +#define MANGOS_RANDOMMOTIONGENERATOR_H + +#include "MovementGenerator.h" +#include "DestinationHolder.h" +#include "Traveller.h" + +#define MAX_CONF_WAYPOINTS 24 + +template +class MANGOS_DLL_SPEC ConfusedMovementGenerator +: public MovementGeneratorMedium< T, ConfusedMovementGenerator > +{ + public: + explicit ConfusedMovementGenerator() : i_nextMoveTime(0) {} + + void Initialize(T &); + void Finalize(T &); + void Reset(T &); + bool Update(T &, const uint32 &); + + MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; } + private: + void _InitSpecific(T &, bool &, bool &); + TimeTracker i_nextMoveTime; + float i_waypoints[MAX_CONF_WAYPOINTS+1][3]; + DestinationHolder< Traveller > i_destinationHolder; + uint32 i_nextMove; +}; +#endif diff --git a/src/game/Corpse.cpp b/src/game/Corpse.cpp new file mode 100644 index 00000000000..785c6b4f784 --- /dev/null +++ b/src/game/Corpse.cpp @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Corpse.h" +#include "Player.h" +#include "UpdateMask.h" +#include "MapManager.h" +#include "ObjectAccessor.h" +#include "Database/DatabaseEnv.h" +#include "Opcodes.h" +#include "WorldSession.h" +#include "WorldPacket.h" +#include "GossipDef.h" +#include "World.h" + +Corpse::Corpse(CorpseType type) : WorldObject() +{ + m_objectType |= TYPEMASK_CORPSE; + m_objectTypeId = TYPEID_CORPSE; + // 2.3.2 - 0x58 + m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION); + + m_valuesCount = CORPSE_END; + + m_type = type; + + m_time = time(NULL); + + lootForBody = false; +} + +Corpse::~Corpse() +{ +} + +void Corpse::AddToWorld() +{ + ///- Register the corpse for guid lookup + if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this); + Object::AddToWorld(); +} + +void Corpse::RemoveFromWorld() +{ + ///- Remove the corpse from the accessor + if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this); + Object::RemoveFromWorld(); +} + +bool Corpse::Create( uint32 guidlow ) +{ + Object::_Create(guidlow, 0, HIGHGUID_CORPSE); + return true; +} + +bool Corpse::Create( uint32 guidlow, Player *owner, uint32 mapid, float x, float y, float z, float ang ) +{ + SetInstanceId(owner->GetInstanceId()); + + WorldObject::_Create(guidlow, HIGHGUID_CORPSE, mapid); + + Relocate(x,y,z,ang); + + if(!IsPositionValid()) + { + sLog.outError("ERROR: Corpse (guidlow %d, owner %s) not created. Suggested coordinates isn't valid (X: %d Y: ^%d)",guidlow,owner->GetName(),x,y); + return false; + } + + SetFloatValue( OBJECT_FIELD_SCALE_X, 1 ); + SetFloatValue( CORPSE_FIELD_POS_X, x ); + SetFloatValue( CORPSE_FIELD_POS_Y, y ); + SetFloatValue( CORPSE_FIELD_POS_Z, z ); + SetFloatValue( CORPSE_FIELD_FACING, ang ); + SetUInt64Value( CORPSE_FIELD_OWNER, owner->GetGUID() ); + + m_grid = MaNGOS::ComputeGridPair(GetPositionX(), GetPositionY()); + + return true; +} + +void Corpse::SaveToDB() +{ + // prevent DB data inconsistance problems and duplicates + CharacterDatabase.BeginTransaction(); + DeleteFromDB(); + + std::ostringstream ss; + ss << "INSERT INTO corpse (guid,player,position_x,position_y,position_z,orientation,zone,map,data,time,corpse_type,instance) VALUES (" + << GetGUIDLow() << ", " << GUID_LOPART(GetOwnerGUID()) << ", " << GetPositionX() << ", " << GetPositionY() << ", " << GetPositionZ() << ", " + << GetOrientation() << ", " << GetZoneId() << ", " << GetMapId() << ", '"; + for(uint16 i = 0; i < m_valuesCount; i++ ) + ss << GetUInt32Value(i) << " "; + ss << "'," << uint64(m_time) <<", " << uint32(GetType()) << ", " << int(GetInstanceId()) << ")"; + CharacterDatabase.Execute( ss.str().c_str() ); + CharacterDatabase.CommitTransaction(); +} + +void Corpse::DeleteBonesFromWorld() +{ + assert(GetType()==CORPSE_BONES); + Corpse* corpse = ObjectAccessor::GetCorpse(*this, GetGUID()); + + if (!corpse) + { + sLog.outError("Bones %u not found in world.", GetGUIDLow()); + return; + } + + AddObjectToRemoveList(); +} + +void Corpse::DeleteFromDB() +{ + if(GetType() == CORPSE_BONES) + // only specific bones + CharacterDatabase.PExecute("DELETE FROM corpse WHERE guid = '%d'", GetGUIDLow()); + else + // all corpses (not bones) + CharacterDatabase.PExecute("DELETE FROM corpse WHERE player = '%d' AND corpse_type <> '0'", GUID_LOPART(GetOwnerGUID())); +} + +bool Corpse::LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId) +{ + bool external = (result != NULL); + if (!external) + // 0 1 2 3 4 5 6 7 8 + result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,data,time,corpse_type,instance FROM corpse WHERE guid = '%u'",guid); + + if( ! result ) + { + sLog.outError("ERROR: Corpse (GUID: %u) not found in table `corpse`, can't load. ",guid); + return false; + } + + Field *fields = result->Fetch(); + + if(!LoadFromDB(guid,fields)) + { + if (!external) delete result; + return false; + } + + if (!external) delete result; + return true; +} + +bool Corpse::LoadFromDB(uint32 guid, Field *fields) +{ + // 0 1 2 3 4 5 6 7 8 + //result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,data,time,corpse_type,instance FROM corpse WHERE guid = '%u'",guid); + float positionX = fields[0].GetFloat(); + float positionY = fields[1].GetFloat(); + float positionZ = fields[2].GetFloat(); + float ort = fields[3].GetFloat(); + uint32 mapid = fields[4].GetUInt32(); + + if(!LoadValues( fields[5].GetString() )) + { + sLog.outError("ERROR: Corpse #%d have broken data in `data` field. Can't be loaded.",guid); + return false; + } + + m_time = time_t(fields[6].GetUInt64()); + m_type = CorpseType(fields[7].GetUInt32()); + if(m_type >= MAX_CORPSE_TYPE) + { + sLog.outError("ERROR: Corpse (guidlow %d, owner %d) have wrong corpse type, not load.",GetGUIDLow(),GUID_LOPART(GetOwnerGUID())); + return false; + } + uint32 instanceid = fields[8].GetUInt32(); + + // overwrite possible wrong/corrupted guid + SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_CORPSE)); + + // place + SetInstanceId(instanceid); + SetMapId(mapid); + Relocate(positionX,positionY,positionZ,ort); + + if(!IsPositionValid()) + { + sLog.outError("ERROR: Corpse (guidlow %d, owner %d) not created. Suggested coordinates isn't valid (X: %d Y: ^%d)",GetGUIDLow(),GUID_LOPART(GetOwnerGUID()),GetPositionX(),GetPositionY()); + return false; + } + + m_grid = MaNGOS::ComputeGridPair(GetPositionX(), GetPositionY()); + + return true; +} + +bool Corpse::isVisibleForInState(Player const* u, bool inVisibleList) const +{ + return IsInWorld() && u->IsInWorld() && IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f)); +} diff --git a/src/game/Corpse.h b/src/game/Corpse.h new file mode 100644 index 00000000000..f99523779a7 --- /dev/null +++ b/src/game/Corpse.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_CORPSE_H +#define MANGOSSERVER_CORPSE_H + +#include "Object.h" +#include "Database/DatabaseEnv.h" +#include "GridDefines.h" +#include "LootMgr.h" + +enum CorpseType +{ + CORPSE_BONES = 0, + CORPSE_RESURRECTABLE_PVE = 1, + CORPSE_RESURRECTABLE_PVP = 2 +}; +#define MAX_CORPSE_TYPE 3 + +// Value equal client resurrection dialog show radius. +#define CORPSE_RECLAIM_RADIUS 39 + +enum CorpseFlags +{ + CORPSE_FLAG_NONE = 0x00, + CORPSE_FLAG_BONES = 0x01, + CORPSE_FLAG_UNK1 = 0x02, + CORPSE_FLAG_UNK2 = 0x04, + CORPSE_FLAG_HIDE_HELM = 0x08, + CORPSE_FLAG_HIDE_CLOAK = 0x10, + CORPSE_FLAG_LOOTABLE = 0x20 +}; + +class Corpse : public WorldObject +{ + public: + explicit Corpse( CorpseType type = CORPSE_BONES ); + ~Corpse( ); + + void AddToWorld(); + void RemoveFromWorld(); + + bool Create( uint32 guidlow ); + bool Create( uint32 guidlow, Player *owner, uint32 mapid, float x, float y, float z, float ang ); + + void SaveToDB(); + bool LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId); + bool LoadFromDB(uint32 guid, Field *fields); + + void DeleteBonesFromWorld(); + void DeleteFromDB(); + + uint64 const& GetOwnerGUID() const { return GetUInt64Value(CORPSE_FIELD_OWNER); } + + time_t const& GetGhostTime() const { return m_time; } + void ResetGhostTime() { m_time = time(NULL); } + CorpseType GetType() const { return m_type; } + + GridPair const& GetGrid() const { return m_grid; } + void SetGrid(GridPair const& grid) { m_grid = grid; } + + bool isVisibleForInState(Player const* u, bool inVisibleList) const; + + Loot loot; // remove insignia ONLY at BG + Player* lootRecipient; + bool lootForBody; + + 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); } + + GridReference &GetGridRef() { return m_gridRef; } + private: + GridReference m_gridRef; + + CorpseType m_type; + time_t m_time; + GridPair m_grid; // gride for corpse position for fast search +}; +#endif diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp new file mode 100644 index 00000000000..bab0c16b4e9 --- /dev/null +++ b/src/game/Creature.cpp @@ -0,0 +1,1993 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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" + +Creature::Creature() : +Unit(), i_AI(NULL), +lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0), +m_itemsLoaded(false), m_trainerSpellsLoaded(false), m_trainer_type(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_NPCTextId(0), 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_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_trainer_spells.clear(); + m_vendor_items.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::LoadTrainerSpells() +{ + if(m_trainerSpellsLoaded) + return; + + m_trainer_spells.clear(); + m_trainer_type = 0; + + Field *fields; + QueryResult *result = WorldDatabase.PQuery("SELECT spell,spellcost,reqskill,reqskillvalue,reqlevel FROM npc_trainer WHERE entry = '%u'", GetEntry()); + + if(!result) + return; + + do + { + fields = result->Fetch(); + + uint32 spellid = fields[0].GetUInt32(); + SpellEntry const *spellinfo = sSpellStore.LookupEntry(spellid); + + if(!spellinfo) + { + sLog.outErrorDb("Trainer (Entry: %u ) has non existing spell %u",GetEntry(),spellid); + continue; + } + + if(!SpellMgr::IsSpellValid(spellinfo)) + { + sLog.outErrorDb("LoadTrainerSpells: Trainer (Entry: %u) has broken learning spell %u.", GetEntry(), spellid); + continue; + } + + if(SpellMgr::IsProfessionSpell(spellinfo->Id)) + m_trainer_type = 2; + + TrainerSpell tspell; + tspell.spell = spellinfo; + tspell.spellcost = fields[1].GetUInt32(); + tspell.reqskill = fields[2].GetUInt32(); + tspell.reqskillvalue= fields[3].GetUInt32(); + tspell.reqlevel = fields[4].GetUInt32(); + + m_trainer_spells.push_back(tspell); + + } while( result->NextRow() ); + + delete result; + + m_trainerSpellsLoaded = true; +} + +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; + } + } + + return bResult; +} + +bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const +{ + if(!isTrainer()) + return false; + + if(m_trainer_spells.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: + // load vendor items if not yet + LoadGoods(); + + if(!GetItemCount()) + { + 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: + // Lazy loading at first access + LoadTrainerSpells(); + + 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->GetGossipMenu()->MenuItemCount()==0 && !pm->GetQuestMenu()->MenuItemCount()) + { + if(HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER)) + { + LoadTrainerSpells(); // Lazy loading at first access + 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->MenuItemCount() == 0 && 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(); + 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() +{ + // already loaded and cached + if(m_NPCTextId) + return m_NPCTextId; + + QueryResult* result = WorldDatabase.PQuery("SELECT textid FROM npc_gossip WHERE npc_guid= '%u'", m_DBTableGuid); + if(result) + { + Field *fields = result->Fetch(); + m_NPCTextId = fields[0].GetUInt32(); + delete result; + } + else + m_NPCTextId = DEFAULT_GOSSIP_MESSAGE; + + return m_NPCTextId; +} + +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 + 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); + + m_DBTableGuid = guidlow; + 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; + } + + uint32 stored_guid = 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_DBTableGuid = stored_guid; + LoadCreaturesAddon(); + + 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]); + } +} + +void Creature::LoadGoods() +{ + // already loaded; + if(m_itemsLoaded) + return; + + m_vendor_items.clear(); + + QueryResult *result = WorldDatabase.PQuery("SELECT item, maxcount, incrtime, ExtendedCost FROM npc_vendor WHERE entry = '%u'", GetEntry()); + + if(!result) + return; + + do + { + Field *fields = result->Fetch(); + + if (GetItemCount() >= MAX_VENDOR_ITEMS) + { + sLog.outErrorDb( "Vendor %u has too many items (%u >= %i). Check the DB!", GetEntry(), GetItemCount(), MAX_VENDOR_ITEMS ); + break; + } + + uint32 item_id = fields[0].GetUInt32(); + if(!sItemStorage.LookupEntry(item_id)) + { + sLog.outErrorDb("Vendor %u have in item list non-existed item %u",GetEntry(),item_id); + continue; + } + + uint32 ExtendedCost = fields[3].GetUInt32(); + if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost)) + sLog.outErrorDb("Item (Entry: %u) has wrong ExtendedCost (%u) for vendor (%u)",item_id,ExtendedCost,GetEntry()); + + AddItem( item_id, fields[1].GetUInt32(), fields[2].GetUInt32(), ExtendedCost); + } + while( result->NextRow() ); + + delete result; + + m_itemsLoaded = true; +} + +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() +{ + 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) + { + 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::CallAssistence() +{ + if( !m_AlreadyCallAssistence && getVictim() && !isPet() && !isCharmed()) + { + SetNoCallAssistence(true); + + float radius = sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS); + if(radius > 0) + { + std::list 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 searcher(assistList, u_check); + + TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); + + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_creature_searcher, *MapManager::Instance().GetMap(GetMapId(), this)); + } + + for(std::list::iterator iter = assistList.begin(); iter != assistList.end(); ++iter) + { + (*iter)->SetNoCallAssistence(true); + if((*iter)->AI()) + (*iter)->AI()->AttackStart(getVictim()); + } + } + } +} + +void Creature::SaveRespawnTime() +{ + if(isPet()) + 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(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(CreatureData const* data = objmgr.GetCreatureData(GetDBTableGUIDLow())) + { + x = data->posX; + y = data->posY; + z = data->posZ; + if(ori) + *ori = data->orientation; + if(dist) + *dist = data->spawndist; + } + else + { + 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; +} diff --git a/src/game/Creature.h b/src/game/Creature.h new file mode 100644 index 00000000000..d70954acefe --- /dev/null +++ b/src/game/Creature.h @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef 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; +}; + +struct CreatureItem +{ + CreatureItem(uint32 _item, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost) + : id(_item), count(_maxcount), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost), lastincr((uint32)time(NULL)) {} + + uint32 id; + uint32 count; + uint32 maxcount; + uint32 incrtime; + uint32 lastincr; + uint32 ExtendedCost; +}; + +struct TrainerSpell +{ + SpellEntry const* spell; + uint32 spellcost; + uint32 reqskill; + uint32 reqskillvalue; + uint32 reqlevel; +}; + +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 Name; + std::vector 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 + +typedef std::list GossipOptionList; + +typedef std::map 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); + + /*********************************************************/ + /*** VENDOR SYSTEM ***/ + /*********************************************************/ + void LoadGoods(); // must be called before access to vendor items, lazy loading at first call + void ReloadGoods() { m_itemsLoaded = false; LoadGoods(); } + + CreatureItem* GetItem(uint32 slot) + { + if(slot>=m_vendor_items.size()) return NULL; + return &m_vendor_items[slot]; + } + uint8 GetItemCount() const { return m_vendor_items.size(); } + void AddItem( uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost) + { + m_vendor_items.push_back(CreatureItem(item, maxcount, ptime, ExtendedCost)); + } + bool RemoveItem( uint32 item_id ) + { + for(CreatureItems::iterator i = m_vendor_items.begin(); i != m_vendor_items.end(); ++i ) + { + if(i->id==item_id) + { + m_vendor_items.erase(i); + return true; + } + } + return false; + } + CreatureItem* FindItem(uint32 item_id) + { + for(CreatureItems::iterator i = m_vendor_items.begin(); i != m_vendor_items.end(); ++i ) + if(i->id==item_id) + return &*i; + return NULL; + } + + /*********************************************************/ + /*** TRAINER SYSTEM ***/ + /*********************************************************/ + typedef std::list SpellsList; + void LoadTrainerSpells(); // must be called before access to trainer spells, lazy loading at first call + void ReloadTrainerSpells() { m_trainerSpellsLoaded = false; LoadTrainerSpells(); } + SpellsList const& GetTrainerSpells() const { return m_trainer_spells; } + uint32 GetTrainerType() const { return m_trainer_type; } + + 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; } + + 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 &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 + typedef std::vector CreatureItems; + CreatureItems m_vendor_items; + bool m_itemsLoaded; // vendor items loading state + + // trainer spells + bool m_trainerSpellsLoaded; // trainer spells loading state + SpellsList m_trainer_spells; + uint32 m_trainer_type; // trainer type based at trainer spells, can be different from creature_template value. + // req. for correct show non-prof. trainers like weaponmaster. + 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; + uint32 m_NPCTextId; // cached value + + 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; + 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 m_gridRef; + CreatureInfo const* m_creatureInfo; // in heroic mode can different from ObjMgr::GetCreatureTemplate(GetEntry()) +}; +#endif diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp new file mode 100644 index 00000000000..06b3d447327 --- /dev/null +++ b/src/game/CreatureAI.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "CreatureAI.h" +#include "HateMatrix.h" + +CreatureAI::~CreatureAI() +{ +} + +uint32 HateBinder::si_noHateValue=0; diff --git a/src/game/CreatureAI.h b/src/game/CreatureAI.h new file mode 100644 index 00000000000..185fb571bb2 --- /dev/null +++ b/src/game/CreatureAI.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CREATUREAI_H +#define MANGOS_CREATUREAI_H + +#include "Common.h" +#include "Platform/Define.h" +#include "Policies/Singleton.h" +#include "Dynamic/ObjectRegistry.h" +#include "Dynamic/FactoryHolder.h" + +class Unit; +class Creature; +struct SpellEntry; + +#define TIME_INTERVAL_LOOK 5000 +#define VISIBILITY_RANGE 10000 + +class MANGOS_DLL_SPEC CreatureAI +{ + public: + + virtual ~CreatureAI(); + + // Called if IsVisible(Unit *who) is true at each *who move + virtual void MoveInLineOfSight(Unit *) = 0; + + // Called at each attack of m_creature by any victim + virtual void AttackStart(Unit *) = 0; + + // Called at stopping attack by any attacker + virtual void EnterEvadeMode() = 0; + + // Called at any heal cast/item used (call non implemented) + virtual void HealBy(Unit * /*healer*/, uint32 /*amount_healed*/) {} + + // Called at any Damage to any victim (before damage apply) + virtual void DamageDeal(Unit * /*done_to*/, uint32 & /*damage*/) {} + + // Called at any Damage from any attacker (before damage apply) + virtual void DamageTaken(Unit *done_by, uint32 & /*damage*/) { AttackedBy(done_by); } + + // Is unit visible for MoveInLineOfSight + virtual bool IsVisible(Unit *) const = 0; + + // Called at World update tick + virtual void UpdateAI(const uint32 diff ) = 0; + + // Called when the creature is killed + virtual void JustDied(Unit *) {} + + // Called when the creature kills a unit + virtual void KilledUnit(Unit *) {} + + // Called when the creature summon successfully other creature + virtual void JustSummoned(Creature* ) {} + + virtual void SummonedCreatureDespawn(Creature* /*unit*/) {} + + // Called when hit by a spell + virtual void SpellHit(Unit*, const SpellEntry*) {} + + // Called when vitim entered water and creature can not enter water + virtual bool canReachByRangeAttack(Unit*) { return false; } + + // Called when the creature is attacked + virtual void AttackedBy(Unit * /*attacker*/) {} + + // Called when creature is spawned or respawned (for reseting variables) + virtual void JustRespawned() {} + + // Called at waypoint reached or point movement finished + virtual void MovementInform(uint32 /*MovementType*/, uint32 /*Data*/) {} +}; + +struct SelectableAI : public FactoryHolder, public Permissible +{ + + SelectableAI(const char *id) : FactoryHolder(id) {} +}; + +template +struct CreatureAIFactory : public SelectableAI +{ + CreatureAIFactory(const char *name) : SelectableAI(name) {} + + CreatureAI* Create(void *) const; + + int Permit(const Creature *c) const { return REAL_AI::Permissible(c); } +}; + +enum Permitions +{ + PERMIT_BASE_NO = -1, + PERMIT_BASE_IDLE = 1, + PERMIT_BASE_REACTIVE = 100, + PERMIT_BASE_PROACTIVE = 200, + PERMIT_BASE_FACTION_SPECIFIC = 400, + PERMIT_BASE_SPECIAL = 800 +}; + +typedef FactoryHolder CreatureAICreator; +typedef FactoryHolder::FactoryHolderRegistry CreatureAIRegistry; +typedef FactoryHolder::FactoryHolderRepository CreatureAIRepository; +#endif diff --git a/src/game/CreatureAIImpl.h b/src/game/CreatureAIImpl.h new file mode 100644 index 00000000000..65ac4708238 --- /dev/null +++ b/src/game/CreatureAIImpl.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef CREATUREAIIMPL_H +#define CREATUREAIIMPL_H + +#include "CreatureAI.h" + +template +inline CreatureAI* +CreatureAIFactory::Create(void *data) const +{ + Creature* creature = reinterpret_cast(data); + return (new REAL_AI(*creature)); +} +#endif diff --git a/src/game/CreatureAIRegistry.cpp b/src/game/CreatureAIRegistry.cpp new file mode 100644 index 00000000000..3a5e3dcfc91 --- /dev/null +++ b/src/game/CreatureAIRegistry.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "CreatureAIRegistry.h" +#include "NullCreatureAI.h" +#include "ReactorAI.h" +#include "AggressorAI.h" +#include "GuardAI.h" +#include "PetAI.h" +#include "TotemAI.h" +#include "RandomMovementGenerator.h" +#include "CreatureAIImpl.h" +#include "MovementGeneratorImpl.h" +#include "MapManager.h" +#include "CreatureAIRegistry.h" +#include "WaypointMovementGenerator.h" + +namespace AIRegistry +{ + void Initialize() + { + (new CreatureAIFactory("NullAI"))->RegisterSelf(); + (new CreatureAIFactory("AggressorAI"))->RegisterSelf(); + (new CreatureAIFactory("ReactorAI"))->RegisterSelf(); + (new CreatureAIFactory("GuardAI"))->RegisterSelf(); + (new CreatureAIFactory("PetAI"))->RegisterSelf(); + (new CreatureAIFactory("TotemAI"))->RegisterSelf(); + + (new MovementGeneratorFactory >(RANDOM_MOTION_TYPE))->RegisterSelf(); + (new MovementGeneratorFactory >(WAYPOINT_MOTION_TYPE))->RegisterSelf(); + } +} diff --git a/src/game/CreatureAIRegistry.h b/src/game/CreatureAIRegistry.h new file mode 100644 index 00000000000..a254b6718b2 --- /dev/null +++ b/src/game/CreatureAIRegistry.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CREATUREAIREGISTRY_H +#define MANGOS_CREATUREAIREGISTRY_H + +namespace AIRegistry +{ + void Initialize(void); +} +#endif diff --git a/src/game/CreatureAISelector.cpp b/src/game/CreatureAISelector.cpp new file mode 100644 index 00000000000..2e8b6f1a230 --- /dev/null +++ b/src/game/CreatureAISelector.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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() ) + ainame="GuardAI"; + else if(creature->isPet() || creature->isCharmed()) + ainame="PetAI"; + else if(creature->isTotem()) + ainame="TotemAI"; + + ai_factory = ai_registry.GetRegistryItem( ainame.c_str() ); + } + + // 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(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 l; + mv_registry.GetRegisteredItems(l); + for( std::vector::iterator iter = l.begin(); iter != l.end(); ++iter) + { + const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str()); + const SelectableMovement *p = dynamic_cast(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/CreatureAISelector.h b/src/game/CreatureAISelector.h new file mode 100644 index 00000000000..328850dbc3d --- /dev/null +++ b/src/game/CreatureAISelector.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_CREATUREAISELECTOR_H +#define MANGOS_CREATUREAISELECTOR_H + +class CreatureAI; +class Creature; +class MovementGenerator; + +namespace FactorySelector +{ + CreatureAI* selectAI(Creature *); + MovementGenerator* selectMovementGenerator(Creature *); +} +#endif diff --git a/src/game/DestinationHolder.cpp b/src/game/DestinationHolder.cpp new file mode 100644 index 00000000000..2c427ad7702 --- /dev/null +++ b/src/game/DestinationHolder.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DestinationHolder.h" diff --git a/src/game/DestinationHolder.h b/src/game/DestinationHolder.h new file mode 100644 index 00000000000..93705bb67cb --- /dev/null +++ b/src/game/DestinationHolder.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_DESTINATION_HOLDER_H +#define MANGOS_DESTINATION_HOLDER_H + +#include "Platform/Define.h" +#include "Timer.h" + +class WorldObject; + +#define TRAVELLER_UPDATE_INTERVAL 300 + +template +class MANGOS_DLL_DECL DestinationHolder +{ + TimeTrackerSmall i_tracker; + uint32 i_totalTravelTime; + uint32 i_timeElapsed; + bool i_destSet; + float i_fromX, i_fromY, i_fromZ; + float i_destX, i_destY, i_destZ; + + public: + DestinationHolder() : i_tracker(TRAVELLER_UPDATE_INTERVAL), i_totalTravelTime(0), i_timeElapsed(0), + i_destSet(false), i_fromX(0), i_fromY(0), i_fromZ(0), i_destX(0), i_destY(0), i_destZ(0) {} + + uint32 SetDestination(TRAVELLER &traveller, float dest_x, float dest_y, float dest_z, bool sendMove = true); + void GetDestination(float &x, float &y, float &z) const { x = i_destX; y = i_destY; z = i_destZ; } + bool UpdateExpired(void) const { return i_tracker.Passed(); } + void ResetUpdate(uint32 t = TRAVELLER_UPDATE_INTERVAL) { i_tracker.Reset(t); } + uint32 GetTotalTravelTime(void) const { return i_totalTravelTime; } + void IncreaseTravelTime(uint32 increment) { i_totalTravelTime += increment; } + bool HasDestination(void) const { return i_destSet; } + float GetDestinationDiff(float x, float y, float z) const; + bool HasArrived(void) const { return (i_totalTravelTime == 0 || i_timeElapsed >= i_totalTravelTime); } + bool UpdateTraveller(TRAVELLER &traveller, uint32 diff, bool force_update=false, bool micro_movement=false); + uint32 StartTravel(TRAVELLER &traveller, bool sendMove = true); + void GetLocationNow(uint32 mapid, float &x, float &y, float &z, bool is3D = false) const; + void GetLocationNowNoMicroMovement(float &x, float &y, float &z) const; // For use without micro movement + float GetDistance2dFromDestSq(const WorldObject &obj) const; + + private: + void _findOffSetPoint(float x1, float y1, float x2, float y2, float offset, float &x, float &y); + +}; +#endif diff --git a/src/game/DestinationHolderImp.h b/src/game/DestinationHolderImp.h new file mode 100644 index 00000000000..8f5efa54b0c --- /dev/null +++ b/src/game/DestinationHolderImp.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_DESTINATIONHOLDERIMP_H +#define MANGOS_DESTINATIONHOLDERIMP_H + +#include "Creature.h" +#include "MapManager.h" +#include "DestinationHolder.h" + +#include + +template +void +DestinationHolder::_findOffSetPoint(float x1, float y1, float x2, float y2, float offset, float &x, float &y) +{ + /* given the point (x1, y1) and (x2, y2).. need to find the point (x,y) on the same line + * such that the distance from (x, y) to (x2, y2) is offset. + * Let the distance of p1 to p2 = d.. then the ratio of offset/d = (x2-x)/(x2-x1) + * hence x = x2 - (offset/d)*(x2-x1) + * like wise offset/d = (y2-y)/(y2-y1); + */ + if( offset == 0 ) + { + x = x2; + y = y2; + } + else + { + double x_diff = double(x2 - x1); + double y_diff = double(y2 - y1); + double distance_d = (double)((x_diff*x_diff) + (y_diff * y_diff)); + if(distance_d == 0) + { + x = x2; + y = y2; + } + else + { + distance_d = ::sqrt(distance_d); // starting distance + double distance_ratio = (double)(distance_d - offset)/(double)distance_d; + // line above has revised formula which is more correct, I think + x = (float)(x1 + (distance_ratio*x_diff)); + y = (float)(y1 + (distance_ratio*y_diff)); + } + } +} + +template +uint32 +DestinationHolder::SetDestination(TRAVELLER &traveller, float dest_x, float dest_y, float dest_z, bool sendMove) +{ + i_destSet = true; + i_destX = dest_x; + i_destY = dest_y; + i_destZ = dest_z; + + return StartTravel(traveller, sendMove); +} + +template +uint32 +DestinationHolder::StartTravel(TRAVELLER &traveller, bool sendMove) +{ + if(!i_destSet) return 0; + + i_fromX = traveller.GetPositionX(); + i_fromY = traveller.GetPositionY(); + i_fromZ = traveller.GetPositionZ(); + + float dx = i_destX - i_fromX; + float dy = i_destY - i_fromY; + float dz = i_destZ - i_fromZ; + + float dist; + //Should be for Creature Flying and Swimming. + if(traveller.GetTraveller().hasUnitState(UNIT_STAT_IN_FLIGHT)) + dist = sqrt((dx*dx) + (dy*dy) + (dz*dz)); + else //Walking on the ground + dist = sqrt((dx*dx) + (dy*dy)); + float speed = traveller.Speed(); + + speed *= 0.001f; // speed is in seconds so convert from second to millisecond + i_totalTravelTime = static_cast(dist/speed); + i_timeElapsed = 0; + if(sendMove) + traveller.MoveTo(i_destX, i_destY, i_destZ, i_totalTravelTime); + return i_totalTravelTime; +} + +template +bool +DestinationHolder::UpdateTraveller(TRAVELLER &traveller, uint32 diff, bool force_update, bool micro_movement) +{ + if(!micro_movement) + { + i_tracker.Update(diff); + i_timeElapsed += diff; + if( i_tracker.Passed() || force_update ) + { + ResetUpdate(); + if(!i_destSet) return true; + float x,y,z; + GetLocationNowNoMicroMovement(x, y, z); + if( x == -431602080 ) + return false; + if( traveller.GetTraveller().GetPositionX() != x || traveller.GetTraveller().GetPositionY() != y ) + { + float ori = traveller.GetTraveller().GetAngle(x, y); + traveller.Relocation(x, y, z, ori); + } + return true; + } + return false; + } + i_tracker.Update(diff); + i_timeElapsed += diff; + if( i_tracker.Passed() || force_update ) + { + ResetUpdate(); + if(!i_destSet) return true; + float x,y,z; + + if(!traveller.GetTraveller().hasUnitState(UNIT_STAT_MOVING | UNIT_STAT_IN_FLIGHT)) + return true; + + if(traveller.GetTraveller().hasUnitState(UNIT_STAT_IN_FLIGHT)) + GetLocationNow(traveller.GetTraveller().GetMapId() ,x, y, z, true); // Should repositione Object with right Coord, so I can bypass some Grid Relocation + else + GetLocationNow(traveller.GetTraveller().GetMapId(), x, y, z, false); + + if( x == -431602080 ) + return false; + + if( traveller.GetTraveller().GetPositionX() != x || traveller.GetTraveller().GetPositionY() != y ) + { + float ori = traveller.GetTraveller().GetAngle(x, y); + traveller.Relocation(x, y, z, ori); + } + // Change movement computation to micro movement based on last tick coords, this makes system work + // even on multiple floors zones without hugh vmaps usage ;) + + // Take care of underrun of uint32 + if (i_totalTravelTime >= i_timeElapsed) + i_totalTravelTime -= i_timeElapsed; // Consider only the remaining part + else + i_totalTravelTime = 0; + + i_timeElapsed = 0; + i_fromX = x; // and change origine + i_fromY = y; // then I take into account only micro movement + i_fromZ = z; + return true; + } + return false; +} + +template +void +DestinationHolder::GetLocationNow(uint32 mapid, float &x, float &y, float &z, bool is3D) const +{ + if( HasArrived() ) + { + x = i_destX; + y = i_destY; + z = i_destZ; + } + else if(HasDestination()) + { + double percent_passed = (double)i_timeElapsed / (double)i_totalTravelTime; + const float distanceX = ((i_destX - i_fromX) * percent_passed); + const float distanceY = ((i_destY - i_fromY) * percent_passed); + const float distanceZ = ((i_destZ - i_fromZ) * percent_passed); + x = i_fromX + distanceX; + y = i_fromY + distanceY; + float z2 = i_fromZ + distanceZ; + // All that is not finished but previous code neither... Traveller need be able to swim. + if(is3D) + z = z2; + else + { + //That part is good for mob Walking on the floor. But the floor is not allways what we thought. + z = MapManager::Instance().GetBaseMap(mapid)->GetHeight(x,y,i_fromZ,false); // Disable cave check + const float groundDist = sqrt(distanceX*distanceX + distanceY*distanceY); + const float zDist = fabs(i_fromZ - z) + 0.000001f; + const float slope = groundDist / zDist; + if(slope < 1.0f) // This prevents the ground returned by GetHeight to be used when in cave + z = z2; // a climb or jump of more than 45 is denied + } + } +} + +template +float +DestinationHolder::GetDistance2dFromDestSq(const WorldObject &obj) const +{ + float x,y,z; + obj.GetPosition(x,y,z); + return (i_destX-x)*(i_destX-x)+(i_destY-y)*(i_destY-y); +} + +template +float +DestinationHolder::GetDestinationDiff(float x, float y, float z) const +{ + return sqrt(((x-i_destX)*(x-i_destX)) + ((y-i_destY)*(y-i_destY)) + ((z-i_destZ)*(z-i_destZ))); +} + +template +void +DestinationHolder::GetLocationNowNoMicroMovement(float &x, float &y, float &z) const +{ + if( HasArrived() ) + { + x = i_destX; + y = i_destY; + z = i_destZ; + } + else + { + double percent_passed = (double)i_timeElapsed / (double)i_totalTravelTime; + x = i_fromX + ((i_destX - i_fromX) * percent_passed); + y = i_fromY + ((i_destY - i_fromY) * percent_passed); + z = i_fromZ + ((i_destZ - i_fromZ) * percent_passed); + } +} + +#endif diff --git a/src/game/DuelHandler.cpp b/src/game/DuelHandler.cpp new file mode 100644 index 00000000000..1019c68d908 --- /dev/null +++ b/src/game/DuelHandler.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "Log.h" +#include "Opcodes.h" +#include "UpdateData.h" +#include "MapManager.h" +#include "Player.h" + +void WorldSession::HandleDuelAcceptedOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,8); + + uint64 guid; + Player *pl; + Player *plTarget; + + if(!GetPlayer()->duel) // ignore accept from duel-sender + return; + + recvPacket >> guid; + + pl = GetPlayer(); + plTarget = pl->duel->opponent; + + if(pl == pl->duel->initiator || !plTarget || pl == plTarget || pl->duel->startTime != 0 || plTarget->duel->startTime != 0) + return; + + //sLog.outDebug( "WORLD: received CMSG_DUEL_ACCEPTED" ); + DEBUG_LOG("Player 1 is: %u (%s)", pl->GetGUIDLow(),pl->GetName()); + DEBUG_LOG("Player 2 is: %u (%s)", plTarget->GetGUIDLow(),plTarget->GetName()); + + time_t now = time(NULL); + pl->duel->startTimer = now; + plTarget->duel->startTimer = now; + + WorldPacket data(SMSG_DUEL_COUNTDOWN, 4); + data << (uint32)3000; // 3 seconds + pl->GetSession()->SendPacket(&data); + plTarget->GetSession()->SendPacket(&data); +} + +void WorldSession::HandleDuelCancelledOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,8); + + //sLog.outDebug( "WORLD: received CMSG_DUEL_CANCELLED" ); + + // no duel requested + if(!GetPlayer()->duel) + return; + + // player surrendered in a duel using /forfeit + if(GetPlayer()->duel->startTime != 0) + { + GetPlayer()->CombatStopWithPets(true); + if(GetPlayer()->duel->opponent) + GetPlayer()->duel->opponent->CombatStopWithPets(true); + + GetPlayer()->CastSpell(GetPlayer(), 7267, true); // beg + GetPlayer()->DuelComplete(DUEL_WON); + return; + } + + // player either discarded the duel using the "discard button" + // or used "/forfeit" before countdown reached 0 + uint64 guid; + recvPacket >> guid; + + GetPlayer()->DuelComplete(DUEL_INTERUPTED); +} diff --git a/src/game/DynamicObject.cpp b/src/game/DynamicObject.cpp new file mode 100644 index 00000000000..d01a6f84a4d --- /dev/null +++ b/src/game/DynamicObject.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "GameObject.h" +#include "UpdateMask.h" +#include "Opcodes.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "ObjectAccessor.h" +#include "Database/DatabaseEnv.h" +#include "SpellAuras.h" +#include "MapManager.h" +#include "GridNotifiers.h" +#include "CellImpl.h" +#include "GridNotifiersImpl.h" + +DynamicObject::DynamicObject() : WorldObject() +{ + m_objectType |= TYPEMASK_DYNAMICOBJECT; + m_objectTypeId = TYPEID_DYNAMICOBJECT; + // 2.3.2 - 0x58 + m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION); + + m_valuesCount = DYNAMICOBJECT_END; +} + +void DynamicObject::AddToWorld() +{ + ///- Register the dynamicObject for guid lookup + if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this); + Object::AddToWorld(); +} + +void DynamicObject::RemoveFromWorld() +{ + ///- Remove the dynamicObject from the accessor + if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this); + Object::RemoveFromWorld(); +} + +bool DynamicObject::Create( uint32 guidlow, Unit *caster, uint32 spellId, uint32 effIndex, float x, float y, float z, int32 duration, float radius ) +{ + SetInstanceId(caster->GetInstanceId()); + + WorldObject::_Create(guidlow, HIGHGUID_DYNAMICOBJECT, caster->GetMapId()); + Relocate(x,y,z,0); + + if(!IsPositionValid()) + { + sLog.outError("ERROR: DynamicObject (spell %u eff %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)",spellId,effIndex,GetPositionX(),GetPositionY()); + return false; + } + + SetUInt32Value( OBJECT_FIELD_ENTRY, spellId ); + SetFloatValue( OBJECT_FIELD_SCALE_X, 1 ); + SetUInt64Value( DYNAMICOBJECT_CASTER, caster->GetGUID() ); + SetUInt32Value( DYNAMICOBJECT_BYTES, 0x00000001 ); + SetUInt32Value( DYNAMICOBJECT_SPELLID, spellId ); + SetFloatValue( DYNAMICOBJECT_RADIUS, radius); + SetFloatValue( DYNAMICOBJECT_POS_X, x ); + SetFloatValue( DYNAMICOBJECT_POS_Y, y ); + SetFloatValue( DYNAMICOBJECT_POS_Z, z ); + SetUInt32Value( DYNAMICOBJECT_CASTTIME, getMSTime() ); // new 2.4.0 + + m_aliveDuration = duration; + m_radius = radius; + m_effIndex = effIndex; + m_spellId = spellId; + m_casterGuid = caster->GetGUID(); + return true; +} + +Unit* DynamicObject::GetCaster() const +{ + // can be not found in some cases + return ObjectAccessor::GetUnit(*this,m_casterGuid); +} + +void DynamicObject::Update(uint32 p_time) +{ + // caster can be not in world at time dynamic object update, but dynamic object not yet deleted in Unit destructor + Unit* caster = GetCaster(); + if(!caster) + { + Delete(); + return; + } + + bool deleteThis = false; + + if(m_aliveDuration > int32(p_time)) + m_aliveDuration -= p_time; + else + deleteThis = true; + + // TODO: make a timer and update this in larger intervals + CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + MaNGOS::DynamicObjectUpdater notifier(*this,caster); + + TypeContainerVisitor world_object_notifier(notifier); + TypeContainerVisitor grid_object_notifier(notifier); + + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, world_object_notifier, *MapManager::Instance().GetMap(GetMapId(), this)); + cell_lock->Visit(cell_lock, grid_object_notifier, *MapManager::Instance().GetMap(GetMapId(), this)); + + if(deleteThis) + { + caster->RemoveDynObjectWithGUID(GetGUID()); + Delete(); + } +} + +void DynamicObject::Delete() +{ + SendObjectDeSpawnAnim(GetGUID()); + AddObjectToRemoveList(); +} + +void DynamicObject::Delay(int32 delaytime) +{ + m_aliveDuration -= delaytime; + for(AffectedSet::iterator iunit= m_affected.begin();iunit != m_affected.end();++iunit) + if (*iunit) + (*iunit)->DelayAura(m_spellId, m_effIndex, delaytime); +} + +bool DynamicObject::isVisibleForInState(Player const* u, bool inVisibleList) const +{ + return IsInWorld() && u->IsInWorld() && IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject()+(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f)); +} diff --git a/src/game/DynamicObject.h b/src/game/DynamicObject.h new file mode 100644 index 00000000000..279a763d4ce --- /dev/null +++ b/src/game/DynamicObject.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_DYNAMICOBJECT_H +#define MANGOSSERVER_DYNAMICOBJECT_H + +#include "Object.h" + +class Unit; +struct SpellEntry; + +class DynamicObject : public WorldObject +{ + public: + typedef std::set AffectedSet; + explicit DynamicObject(); + + void AddToWorld(); + void RemoveFromWorld(); + + bool Create(uint32 guidlow, Unit *caster, uint32 spellId, uint32 effIndex, float x, float y, float z, int32 duration, float radius); + void Update(uint32 p_time); + void Delete(); + uint32 GetSpellId() const { return m_spellId; } + uint32 GetEffIndex() const { return m_effIndex; } + uint32 GetDuration() const { return m_aliveDuration; } + uint64 GetCasterGUID() const { return m_casterGuid; } + Unit* GetCaster() const; + float GetRadius() const { return m_radius; } + bool IsAffecting(Unit *unit) const { return m_affected.find(unit) != m_affected.end(); } + void AddAffected(Unit *unit) { m_affected.insert(unit); } + void RemoveAffected(Unit *unit) { m_affected.erase(unit); } + void Delay(int32 delaytime); + bool isVisibleForInState(Player const* u, bool inVisibleList) const; + + 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); } + + GridReference &GetGridRef() { return m_gridRef; } + protected: + uint64 m_casterGuid; + uint32 m_spellId; + uint32 m_effIndex; + int32 m_aliveDuration; + time_t m_nextThinkTime; + float m_radius; + AffectedSet m_affected; + private: + GridReference m_gridRef; +}; +#endif diff --git a/src/game/FleeingMovementGenerator.cpp b/src/game/FleeingMovementGenerator.cpp new file mode 100644 index 00000000000..80ecf13922f --- /dev/null +++ b/src/game/FleeingMovementGenerator.cpp @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 +void +FleeingMovementGenerator::_setTargetLocation(T &owner) +{ + if( !&owner ) + return; + + if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED) ) + return; + + if(!_setMoveData(owner)) + return; + + float x, y, z; + if(!_getPoint(owner, x, y, z)) + return; + + owner.addUnitState(UNIT_STAT_FLEEING); + Traveller traveller(owner); + i_destinationHolder.SetDestination(traveller, x, y, z); +} + +template +bool +FleeingMovementGenerator::_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 +bool +FleeingMovementGenerator::_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 +void +FleeingMovementGenerator::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::_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::_Init(Player &) +{ + is_water_ok = true; + is_land_ok = true; +} + +template +void +FleeingMovementGenerator::Finalize(T &owner) +{ + owner.clearUnitState(UNIT_STAT_FLEEING); +} + +template +void +FleeingMovementGenerator::Reset(T &owner) +{ + Initialize(owner); +} + +template +bool +FleeingMovementGenerator::Update(T &owner, const uint32 & time_diff) +{ + if( !&owner || !owner.isAlive() ) + return false; + if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED) ) + return true; + + Traveller 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::Initialize(Player &); +template void FleeingMovementGenerator::Initialize(Creature &); +template bool FleeingMovementGenerator::_setMoveData(Player &); +template bool FleeingMovementGenerator::_setMoveData(Creature &); +template bool FleeingMovementGenerator::_getPoint(Player &, float &, float &, float &); +template bool FleeingMovementGenerator::_getPoint(Creature &, float &, float &, float &); +template void FleeingMovementGenerator::_setTargetLocation(Player &); +template void FleeingMovementGenerator::_setTargetLocation(Creature &); +template void FleeingMovementGenerator::Finalize(Player &); +template void FleeingMovementGenerator::Finalize(Creature &); +template void FleeingMovementGenerator::Reset(Player &); +template void FleeingMovementGenerator::Reset(Creature &); +template bool FleeingMovementGenerator::Update(Player &, const uint32 &); +template bool FleeingMovementGenerator::Update(Creature &, const uint32 &); diff --git a/src/game/FleeingMovementGenerator.h b/src/game/FleeingMovementGenerator.h new file mode 100644 index 00000000000..f0b48e27b23 --- /dev/null +++ b/src/game/FleeingMovementGenerator.h @@ -0,0 +1,62 @@ +/* +* Copyright (C) 2005-2008 MaNGOS +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MANGOS_FLEEINGMOVEMENTGENERATOR_H +#define MANGOS_FLEEINGMOVEMENTGENERATOR_H + +#include "MovementGenerator.h" +#include "DestinationHolder.h" +#include "Traveller.h" +#include "MapManager.h" + +template +class MANGOS_DLL_SPEC FleeingMovementGenerator +: public MovementGeneratorMedium< T, FleeingMovementGenerator > +{ + public: + FleeingMovementGenerator(uint64 fright) : i_frightGUID(fright), i_nextCheckTime(0) {} + + void Initialize(T &); + void Finalize(T &); + void Reset(T &); + bool Update(T &, const uint32 &); + + MovementGeneratorType GetMovementGeneratorType() { return FLEEING_MOTION_TYPE; } + + private: + void _setTargetLocation(T &owner); + bool _getPoint(T &owner, float &x, float &y, float &z); + bool _setMoveData(T &owner); + void _Init(T &); + + bool is_water_ok :1; + bool is_land_ok :1; + bool i_only_forward:1; + + float i_caster_x; + float i_caster_y; + float i_caster_z; + float i_last_distance_from_caster; + float i_to_distance_from_caster; + float i_cur_angle; + TimeTracker i_nextCheckTime; + uint64 i_frightGUID; + + DestinationHolder< Traveller > i_destinationHolder; +}; +#endif diff --git a/src/game/FollowerRefManager.h b/src/game/FollowerRefManager.h new file mode 100644 index 00000000000..460a3612ded --- /dev/null +++ b/src/game/FollowerRefManager.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _FOLLOWERREFMANAGER +#define _FOLLOWERREFMANAGER + +#include "Utilities/LinkedReference/RefManager.h" + +class Unit; +class TargetedMovementGeneratorBase; + +class FollowerRefManager : public RefManager +{ + +}; +#endif diff --git a/src/game/FollowerReference.cpp b/src/game/FollowerReference.cpp new file mode 100644 index 00000000000..2ebea8187f5 --- /dev/null +++ b/src/game/FollowerReference.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Unit.h" +#include "TargetedMovementGenerator.h" +#include "FollowerReference.h" + +void FollowerReference::targetObjectBuildLink() +{ + getTarget()->addFollower(this); +} + +void FollowerReference::targetObjectDestroyLink() +{ + getTarget()->removeFollower(this); +} + +void FollowerReference::sourceObjectDestroyLink() +{ + getSource()->stopFollowing(); +} diff --git a/src/game/FollowerReference.h b/src/game/FollowerReference.h new file mode 100644 index 00000000000..3a79d8cedb0 --- /dev/null +++ b/src/game/FollowerReference.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _FOLLOWERREFERENCE_H +#define _FOLLOWERREFERENCE_H + +#include "Utilities/LinkedReference/Reference.h" + +class TargetedMovementGeneratorBase; +class Unit; + +class MANGOS_DLL_SPEC FollowerReference : public Reference +{ + protected: + void targetObjectBuildLink(); + void targetObjectDestroyLink(); + void sourceObjectDestroyLink(); +}; +#endif diff --git a/src/game/Formulas.h b/src/game/Formulas.h new file mode 100644 index 00000000000..a7ef3200e73 --- /dev/null +++ b/src/game/Formulas.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_FORMULAS_H +#define MANGOS_FORMULAS_H + +#include "World.h" + +namespace MaNGOS +{ + namespace XP + { + typedef enum XPColorChar { RED, ORANGE, YELLOW, GREEN, GRAY }; + + inline uint32 GetGrayLevel(uint32 pl_level) + { + if( pl_level <= 5 ) + return 0; + else if( pl_level <= 39 ) + return pl_level - 5 - pl_level/10; + else if( pl_level <= 59 ) + return pl_level - 1 - pl_level/5; + else + return pl_level - 9; + } + + inline XPColorChar GetColorCode(uint32 pl_level, uint32 mob_level) + { + if( mob_level >= pl_level + 5 ) + return RED; + else if( mob_level >= pl_level + 3 ) + return ORANGE; + else if( mob_level >= pl_level - 2 ) + return YELLOW; + else if( mob_level > GetGrayLevel(pl_level) ) + return GREEN; + else + return GRAY; + } + + inline uint32 GetZeroDifference(uint32 pl_level) + { + if( pl_level < 8 ) return 5; + if( pl_level < 10 ) return 6; + if( pl_level < 12 ) return 7; + if( pl_level < 16 ) return 8; + if( pl_level < 20 ) return 9; + if( pl_level < 30 ) return 11; + if( pl_level < 40 ) return 12; + if( pl_level < 45 ) return 13; + if( pl_level < 50 ) return 14; + if( pl_level < 55 ) return 15; + if( pl_level < 60 ) return 16; + return 17; + } + + inline uint32 BaseGain(uint32 pl_level, uint32 mob_level, ContentLevels content) + { + const uint32 nBaseExp = content == CONTENT_1_60 ? 45 : 235; + if( mob_level >= pl_level ) + { + uint32 nLevelDiff = mob_level - pl_level; + if (nLevelDiff > 4) + nLevelDiff = 4; + return ((pl_level*5 + nBaseExp) * (20 + nLevelDiff)/10 + 1)/2; + } + else + { + uint32 gray_level = GetGrayLevel(pl_level); + if( mob_level > gray_level ) + { + uint32 ZD = GetZeroDifference(pl_level); + return (pl_level*5 + nBaseExp) * (ZD + mob_level - pl_level)/ZD; + } + return 0; + } + } + + inline uint32 Gain(Player *pl, Unit *u) + { + if(u->GetTypeId()==TYPEID_UNIT && ( + ((Creature*)u)->isTotem() || ((Creature*)u)->isPet() || + (((Creature*)u)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL) )) + return 0; + + uint32 xp_gain= BaseGain(pl->getLevel(), u->getLevel(), GetContentLevelsForMapAndZone(pl->GetMapId(),pl->GetZoneId())); + if( xp_gain == 0 ) + return 0; + + if(u->GetTypeId()==TYPEID_UNIT && ((Creature*)u)->isElite()) + xp_gain *= 2; + + return (uint32)(xp_gain*sWorld.getRate(RATE_XP_KILL)); + } + + inline uint32 xp_Diff(uint32 lvl) + { + if( lvl < 29 ) + return 0; + if( lvl == 29 ) + return 1; + if( lvl == 30 ) + return 3; + if( lvl == 31 ) + return 6; + else + return (5*(lvl-30)); + } + + inline uint32 mxp(uint32 lvl) + { + if (lvl < 60) + { + return (45 + (5*lvl)); + } + else + { + return (235 + (5*lvl)); + } + } + + inline uint32 xp_to_level(uint32 lvl) + { + uint32 xp = 0; + if (lvl < 60) + { + xp = (8*lvl + xp_Diff(lvl)) * mxp(lvl); + } + else if (lvl == 60) + { + xp = (155 + mxp(lvl) * (1344 - 70 - ((69 - lvl) * (7 + (69 - lvl) * 8 - 1)/2))); + } + else if (lvl < 70) + { + xp = (155 + mxp(lvl) * (1344 - ((69-lvl) * (7 + (69 - lvl) * 8 - 1)/2))); + }else + { + // level higher than 70 is not supported + xp = (uint32)(779700 * (pow(sWorld.getRate(RATE_XP_PAST_70), (int32)lvl - 69))); + return ((xp < 0x7fffffff) ? xp : 0x7fffffff); + } + + // The XP to Level is always rounded to the nearest 100 points (50 rounded to high). + xp = ((xp + 50) / 100) * 100; // use additional () for prevent free association operations in C++ + + if ((lvl > 10) && (lvl < 60)) // compute discount added in 2.3.x + { + uint32 discount = (lvl < 28) ? (lvl - 10) : 18; + xp = (xp * (100 - discount)) / 100; // apply discount + xp = (xp / 100) * 100; // floor to hundreds + } + + return xp; + } + + inline float xp_in_group_rate(uint32 count, bool isRaid) + { + if(isRaid) + { + // FIX ME: must apply decrease modifiers dependent from raid size + return 1.0f; + } + else + { + switch(count) + { + case 0: + case 1: + case 2: + return 1.0f; + case 3: + return 1.166f; + case 4: + return 1.3f; + case 5: + default: + return 1.4f; + } + } + } + } +} +#endif diff --git a/src/game/GameEvent.cpp b/src/game/GameEvent.cpp new file mode 100644 index 00000000000..e36cb6caae8 --- /dev/null +++ b/src/game/GameEvent.cpp @@ -0,0 +1,659 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "GameEvent.h" +#include "World.h" +#include "ObjectMgr.h" +#include "ProgressBar.h" +#include "Language.h" +#include "Log.h" +#include "MapManager.h" +#include "Policies/SingletonImp.h" + +INSTANTIATE_SINGLETON_1(GameEvent); + +bool GameEvent::CheckOneGameEvent(uint16 entry) const +{ + // Get the event information + time_t currenttime = time(NULL); + if( mGameEvent[entry].start < currenttime && currenttime < mGameEvent[entry].end && + ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE)) < (mGameEvent[entry].length * MINUTE) ) + return true; + else + return false; +} + +uint32 GameEvent::NextCheck(uint16 entry) const +{ + time_t currenttime = time(NULL); + + // outdated event: we return max + if (currenttime > mGameEvent[entry].end) + return max_ge_check_delay; + + // never started event, we return delay before start + if (mGameEvent[entry].start > currenttime) + return (mGameEvent[entry].start - currenttime); + + uint32 delay; + // in event, we return the end of it + if ((((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * 60)) < (mGameEvent[entry].length * 60))) + // we return the delay before it ends + delay = (mGameEvent[entry].length * MINUTE) - ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE)); + else // not in window, we return the delay before next start + delay = (mGameEvent[entry].occurence * MINUTE) - ((currenttime - mGameEvent[entry].start) % (mGameEvent[entry].occurence * MINUTE)); + // In case the end is before next check + if (mGameEvent[entry].end < time_t(currenttime + delay)) + return (mGameEvent[entry].end - currenttime); + else + return delay; +} + +void GameEvent::StartEvent( uint16 event_id, bool overwrite ) +{ + AddActiveEvent(event_id); + ApplyNewEvent(event_id); + if(overwrite) + { + mGameEvent[event_id].start = time(NULL); + if(mGameEvent[event_id].end <= mGameEvent[event_id].start) + mGameEvent[event_id].end = mGameEvent[event_id].start+mGameEvent[event_id].length; + } +} + +void GameEvent::StopEvent( uint16 event_id, bool overwrite ) +{ + RemoveActiveEvent(event_id); + UnApplyEvent(event_id); + if(overwrite) + { + mGameEvent[event_id].start = time(NULL) - mGameEvent[event_id].length * MINUTE; + if(mGameEvent[event_id].end <= mGameEvent[event_id].start) + mGameEvent[event_id].end = mGameEvent[event_id].start+mGameEvent[event_id].length; + } +} + +void GameEvent::LoadFromDB() +{ + { + QueryResult *result = WorldDatabase.Query("SELECT MAX(entry) FROM game_event"); + if( !result ) + { + sLog.outString(">> Table game_event is empty."); + sLog.outString(); + return; + } + + Field *fields = result->Fetch(); + + uint32 max_event_id = fields[0].GetUInt16(); + delete result; + + mGameEvent.resize(max_event_id+1); + } + + QueryResult *result = WorldDatabase.Query("SELECT entry,UNIX_TIMESTAMP(start_time),UNIX_TIMESTAMP(end_time),occurence,length,description FROM game_event"); + if( !result ) + { + mGameEvent.clear(); + sLog.outString(">> Table game_event is empty:"); + sLog.outString(); + return; + } + + uint32 count = 0; + + barGoLink bar( result->GetRowCount() ); + do + { + ++count; + Field *fields = result->Fetch(); + + bar.step(); + + uint16 event_id = fields[0].GetUInt16(); + if(event_id==0) + { + sLog.outErrorDb("`game_event` game event id (%i) is reserved and can't be used.",event_id); + continue; + } + + GameEventData& pGameEvent = mGameEvent[event_id]; + uint64 starttime = fields[1].GetUInt64(); + pGameEvent.start = time_t(starttime); + uint64 endtime = fields[2].GetUInt64(); + pGameEvent.end = time_t(endtime); + pGameEvent.occurence = fields[3].GetUInt32(); + pGameEvent.length = fields[4].GetUInt32(); + + if(pGameEvent.length==0) // length>0 is validity check + { + sLog.outErrorDb("`game_event` game event id (%i) have length 0 and can't be used.",event_id); + continue; + } + + pGameEvent.description = fields[5].GetCppString(); + + } while( result->NextRow() ); + + sLog.outString(); + sLog.outString( ">> Loaded %u game events", count ); + delete result; + + mGameEventCreatureGuids.resize(mGameEvent.size()*2-1); + // 1 2 + result = WorldDatabase.Query("SELECT creature.guid, game_event_creature.event " + "FROM creature JOIN game_event_creature ON creature.guid = game_event_creature.guid"); + + count = 0; + if( !result ) + { + barGoLink bar2(1); + bar2.step(); + + sLog.outString(); + sLog.outString(">> Loaded %u creatures in game events", count ); + } + else + { + + barGoLink bar2( result->GetRowCount() ); + do + { + Field *fields = result->Fetch(); + + bar2.step(); + + uint32 guid = fields[0].GetUInt32(); + int16 event_id = fields[1].GetInt16(); + + int32 internal_event_id = mGameEvent.size() + event_id - 1; + + if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size()) + { + sLog.outErrorDb("`game_event_creature` game event id (%i) is out of range compared to max event id in `game_event`",event_id); + continue; + } + + ++count; + GuidList& crelist = mGameEventCreatureGuids[internal_event_id]; + crelist.push_back(guid); + + } while( result->NextRow() ); + sLog.outString(); + sLog.outString( ">> Loaded %u creatures in game events", count ); + delete result; + } + + mGameEventGameobjectGuids.resize(mGameEvent.size()*2-1); + // 1 2 + result = WorldDatabase.Query("SELECT gameobject.guid, game_event_gameobject.event " + "FROM gameobject JOIN game_event_gameobject ON gameobject.guid=game_event_gameobject.guid"); + + count = 0; + if( !result ) + { + barGoLink bar3(1); + bar3.step(); + + sLog.outString(); + sLog.outString(">> Loaded %u gameobjects in game events", count ); + } + else + { + + barGoLink bar3( result->GetRowCount() ); + do + { + Field *fields = result->Fetch(); + + bar3.step(); + + uint32 guid = fields[0].GetUInt32(); + int16 event_id = fields[1].GetInt16(); + + int32 internal_event_id = mGameEvent.size() + event_id - 1; + + if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size()) + { + sLog.outErrorDb("`game_event_gameobject` game event id (%i) is out of range compared to max event id in `game_event`",event_id); + continue; + } + + ++count; + GuidList& golist = mGameEventGameobjectGuids[internal_event_id]; + golist.push_back(guid); + + } while( result->NextRow() ); + sLog.outString(); + sLog.outString( ">> Loaded %u gameobjects in game events", count ); + + delete result; + } + + mGameEventModelEquip.resize(mGameEvent.size()); + // 0 1 2 + result = WorldDatabase.Query("SELECT creature.guid, game_event_model_equip.event, game_event_model_equip.modelid," + // 3 + "game_event_model_equip.equipment_id " + "FROM creature JOIN game_event_model_equip ON creature.guid=game_event_model_equip.guid"); + + count = 0; + if( !result ) + { + barGoLink bar3(1); + bar3.step(); + + sLog.outString(); + sLog.outString(">> Loaded %u model/equipment changes in game events", count ); + } + else + { + + barGoLink bar3( result->GetRowCount() ); + do + { + Field *fields = result->Fetch(); + + bar3.step(); + uint32 guid = fields[0].GetUInt32(); + uint16 event_id = fields[1].GetUInt16(); + + if(event_id >= mGameEventModelEquip.size()) + { + sLog.outErrorDb("`game_event_model_equip` game event id (%u) is out of range compared to max event id in `game_event`",event_id); + continue; + } + + ++count; + ModelEquipList& equiplist = mGameEventModelEquip[event_id]; + ModelEquip newModelEquipSet; + newModelEquipSet.modelid = fields[2].GetUInt32(); + newModelEquipSet.equipment_id = fields[3].GetUInt32(); + newModelEquipSet.equipement_id_prev = 0; + newModelEquipSet.modelid_prev = 0; + + if(newModelEquipSet.equipment_id > 0) + { + if(!objmgr.GetEquipmentInfo(newModelEquipSet.equipment_id)) + { + sLog.outErrorDb("Table `game_event_model_equip` have creature (Guid: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", guid, newModelEquipSet.equipment_id); + continue; + } + } + + equiplist.push_back(std::pair(guid, newModelEquipSet)); + + } while( result->NextRow() ); + sLog.outString(); + sLog.outString( ">> Loaded %u model/equipment changes in game events", count ); + + delete result; + } + + mGameEventQuests.resize(mGameEvent.size()); + // 0 1 2 + result = WorldDatabase.Query("SELECT id, quest, event FROM game_event_creature_quest"); + + count = 0; + if( !result ) + { + barGoLink bar3(1); + bar3.step(); + + sLog.outString(); + sLog.outString(">> Loaded %u quests additions in game events", count ); + } + else + { + + barGoLink bar3( result->GetRowCount() ); + do + { + Field *fields = result->Fetch(); + + bar3.step(); + uint32 id = fields[0].GetUInt32(); + uint32 quest = fields[1].GetUInt32(); + uint16 event_id = fields[2].GetUInt16(); + + if(event_id >= mGameEventQuests.size()) + { + sLog.outErrorDb("`game_event_creature_quest` game event id (%u) is out of range compared to max event id in `game_event`",event_id); + continue; + } + + ++count; + QuestRelList& questlist = mGameEventQuests[event_id]; + questlist.push_back(QuestRelation(id, quest)); + + } while( result->NextRow() ); + sLog.outString(); + sLog.outString( ">> Loaded %u quests additions in game events", count ); + + delete result; + } +} + +uint32 GameEvent::Initialize() // return the next event delay in ms +{ + m_ActiveEvents.clear(); + uint32 delay = Update(); + sLog.outBasic("Game Event system initialized." ); + isSystemInit = true; + return delay; +} + +uint32 GameEvent::Update() // return the next event delay in ms +{ + uint32 nextEventDelay = max_ge_check_delay; // 1 day + uint32 calcDelay; + for (uint16 itr = 1; itr < mGameEvent.size(); itr++) + { + //sLog.outErrorDb("Checking event %u",itr); + if (CheckOneGameEvent(itr)) + { + //sLog.outDebug("GameEvent %u is active",itr->first); + if (!IsActiveEvent(itr)) + StartEvent(itr); + } + else + { + //sLog.outDebug("GameEvent %u is not active",itr->first); + if (IsActiveEvent(itr)) + StopEvent(itr); + else + { + if (!isSystemInit) + { + int16 event_nid = (-1) * (itr); + // spawn all negative ones for this event + GameEventSpawn(event_nid); + } + } + } + calcDelay = NextCheck(itr); + if (calcDelay < nextEventDelay) + nextEventDelay = calcDelay; + } + sLog.outBasic("Next game event check in %u seconds.", nextEventDelay + 1); + return (nextEventDelay + 1) * 1000; // Add 1 second to be sure event has started/stopped at next call +} + +void GameEvent::UnApplyEvent(uint16 event_id) +{ + sLog.outString("GameEvent %u \"%s\" removed.", event_id, mGameEvent[event_id].description.c_str()); + // un-spawn positive event tagged objects + GameEventUnspawn(event_id); + // spawn negative event tagget objects + int16 event_nid = (-1) * event_id; + GameEventSpawn(event_nid); + // restore equipment or model + ChangeEquipOrModel(event_id, false); + // Remove quests that are events only to non event npc + UpdateEventQuests(event_id, false); +} + +void GameEvent::ApplyNewEvent(uint16 event_id) +{ + switch(sWorld.getConfig(CONFIG_EVENT_ANNOUNCE)) + { + case 0: // disable + break; + case 1: // announce events + sWorld.SendWorldText(LANG_EVENTMESSAGE, mGameEvent[event_id].description.c_str()); + break; + } + + sLog.outString("GameEvent %u \"%s\" started.", event_id, mGameEvent[event_id].description.c_str()); + // spawn positive event tagget objects + GameEventSpawn(event_id); + // un-spawn negative event tagged objects + int16 event_nid = (-1) * event_id; + GameEventUnspawn(event_nid); + // Change equipement or model + ChangeEquipOrModel(event_id, true); + // Add quests that are events only to non event npc + UpdateEventQuests(event_id, true); +} + +void GameEvent::GameEventSpawn(int16 event_id) +{ + int32 internal_event_id = mGameEvent.size() + event_id - 1; + + if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size()) + { + sLog.outError("GameEvent::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size()); + return; + } + + for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();itr != mGameEventCreatureGuids[internal_event_id].end();++itr) + { + // Add to correct cell + CreatureData const* data = objmgr.GetCreatureData(*itr); + if (data) + { + objmgr.AddCreatureToGrid(*itr, data); + + // Spawn if necessary (loaded grids only) + Map* map = const_cast(MapManager::Instance().GetBaseMap(data->mapid)); + // We use spawn coords to spawn + if(!map->Instanceable() && !map->IsRemovalGrid(data->posX,data->posY)) + { + Creature* pCreature = new Creature; + //sLog.outDebug("Spawning creature %u",*itr); + if (!pCreature->LoadFromDB(*itr, map)) + { + delete pCreature; + } + else + { + map->Add(pCreature); + } + } + } + } + + if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size()) + { + sLog.outError("GameEvent::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size()); + return; + } + + for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();itr != mGameEventGameobjectGuids[internal_event_id].end();++itr) + { + // Add to correct cell + GameObjectData const* data = objmgr.GetGOData(*itr); + if (data) + { + objmgr.AddGameobjectToGrid(*itr, data); + // Spawn if necessary (loaded grids only) + // this base map checked as non-instanced and then only existed + Map* map = const_cast(MapManager::Instance().GetBaseMap(data->mapid)); + // We use current coords to unspawn, not spawn coords since creature can have changed grid + if(!map->Instanceable() && !map->IsRemovalGrid(data->posX, data->posY)) + { + GameObject* pGameobject = new GameObject; + //sLog.outDebug("Spawning gameobject %u", *itr); + if (!pGameobject->LoadFromDB(*itr, map)) + { + delete pGameobject; + } + else + { + if(pGameobject->isSpawnedByDefault()) + map->Add(pGameobject); + } + } + } + } +} + +void GameEvent::GameEventUnspawn(int16 event_id) +{ + int32 internal_event_id = mGameEvent.size() + event_id - 1; + + if(internal_event_id < 0 || internal_event_id >= mGameEventCreatureGuids.size()) + { + sLog.outError("GameEvent::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: %u)",internal_event_id,mGameEventCreatureGuids.size()); + return; + } + + for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();itr != mGameEventCreatureGuids[internal_event_id].end();++itr) + { + // Remove the creature from grid + if( CreatureData const* data = objmgr.GetCreatureData(*itr) ) + { + objmgr.RemoveCreatureFromGrid(*itr, data); + + if( Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(*itr, data->id, HIGHGUID_UNIT), (Creature*)NULL) ) + { + pCreature->CleanupsBeforeDelete(); + pCreature->AddObjectToRemoveList(); + } + } + } + + if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size()) + { + sLog.outError("GameEvent::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %u)",internal_event_id,mGameEventGameobjectGuids.size()); + return; + } + + for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();itr != mGameEventGameobjectGuids[internal_event_id].end();++itr) + { + // Remove the gameobject from grid + if(GameObjectData const* data = objmgr.GetGOData(*itr)) + { + objmgr.RemoveGameobjectFromGrid(*itr, data); + + if( GameObject* pGameobject = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(*itr, data->id, HIGHGUID_GAMEOBJECT), (GameObject*)NULL) ) + pGameobject->AddObjectToRemoveList(); + } + } +} + +void GameEvent::ChangeEquipOrModel(int16 event_id, bool activate) +{ + for(ModelEquipList::iterator itr = mGameEventModelEquip[event_id].begin();itr != mGameEventModelEquip[event_id].end();++itr) + { + // Remove the creature from grid + CreatureData const* data = objmgr.GetCreatureData(itr->first); + if(!data) + continue; + + // Update if spawned + Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(itr->first, data->id,HIGHGUID_UNIT), (Creature*)NULL); + if (pCreature) + { + if (activate) + { + itr->second.equipement_id_prev = pCreature->GetCurrentEquipmentId(); + itr->second.modelid_prev = pCreature->GetDisplayId(); + pCreature->LoadEquipment(itr->second.equipment_id, true); + if (itr->second.modelid >0 && itr->second.modelid_prev != itr->second.modelid) + { + CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(itr->second.modelid); + if (minfo) + { + pCreature->SetDisplayId(itr->second.modelid); + pCreature->SetNativeDisplayId(itr->second.modelid); + pCreature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius); + pCreature->SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach ); + } + } + } + else + { + pCreature->LoadEquipment(itr->second.equipement_id_prev, true); + if (itr->second.modelid_prev >0 && itr->second.modelid_prev != itr->second.modelid) + { + CreatureModelInfo const *minfo = objmgr.GetCreatureModelInfo(itr->second.modelid_prev); + if (minfo) + { + pCreature->SetDisplayId(itr->second.modelid_prev); + pCreature->SetNativeDisplayId(itr->second.modelid_prev); + pCreature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS,minfo->bounding_radius); + pCreature->SetFloatValue(UNIT_FIELD_COMBATREACH,minfo->combat_reach ); + } + } + } + } + else // If not spawned + { + CreatureData const* data = objmgr.GetCreatureData(itr->first); + if (data && activate) + { + CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(data->id); + uint32 display_id = objmgr.ChooseDisplayId(0,cinfo,data); + CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id); + if (minfo) + display_id = minfo->modelid; + if (data->equipmentId == 0) + itr->second.equipement_id_prev = cinfo->equipmentId; + else if (data->equipmentId != -1) + itr->second.equipement_id_prev = data->equipmentId; + itr->second.modelid_prev = display_id; + } + } + // now last step: put in data + // just to have write access to it + CreatureData& data2 = objmgr.NewOrExistCreatureData(itr->first); + if (activate) + { + data2.displayid = itr->second.modelid; + data2.equipmentId = itr->second.equipment_id; + } + else + { + data2.displayid = itr->second.modelid_prev; + data2.equipmentId = itr->second.equipement_id_prev; + } + } +} + +void GameEvent::UpdateEventQuests(uint16 event_id, bool Activate) +{ + QuestRelList::iterator itr; + for (itr = mGameEventQuests[event_id].begin();itr != mGameEventQuests[event_id].end();++itr) + { + QuestRelations &CreatureQuestMap = objmgr.mCreatureQuestRelations; + if (Activate) // Add the pair(id,quest) to the multimap + CreatureQuestMap.insert(QuestRelations::value_type(itr->first, itr->second)); + else + { // Remove the pair(id,quest) from the multimap + QuestRelations::iterator qitr = CreatureQuestMap.find(itr->first); + if (qitr == CreatureQuestMap.end()) + continue; + QuestRelations::iterator lastElement = CreatureQuestMap.upper_bound(itr->first); + for ( ;qitr != lastElement;++qitr) + { + if (qitr->second == itr->second) + { + CreatureQuestMap.erase(qitr); // iterator is now no more valid + break; // but we can exit loop since the element is found + } + } + } + } +} + +GameEvent::GameEvent() +{ + isSystemInit = false; +} diff --git a/src/game/GameEvent.h b/src/game/GameEvent.h new file mode 100644 index 00000000000..535422eb407 --- /dev/null +++ b/src/game/GameEvent.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_GAMEEVENT_H +#define MANGOS_GAMEEVENT_H + +#include "Platform/Define.h" +#include "Creature.h" +#include "GameObject.h" + +#define max_ge_check_delay 86400 // 1 day in seconds + +struct GameEventData +{ + GameEventData() : start(1),end(0),occurence(0),length(0) {} + time_t start; + time_t end; + uint32 occurence; + uint32 length; + std::string description; + + bool isValid() const { return length > 0; } +}; + +struct ModelEquip +{ + uint32 modelid; + uint32 equipment_id; + uint32 modelid_prev; + uint32 equipement_id_prev; +}; + +class GameEvent +{ + public: + GameEvent(); + ~GameEvent() {}; + typedef std::set ActiveEvents; + typedef std::vector GameEventDataMap; + ActiveEvents const& GetActiveEventList() const { return m_ActiveEvents; } + GameEventDataMap const& GetEventMap() const { return mGameEvent; } + bool CheckOneGameEvent(uint16 entry) const; + uint32 NextCheck(uint16 entry) const; + void LoadFromDB(); + uint32 Update(); + bool IsActiveEvent(uint16 event_id) { return ( m_ActiveEvents.find(event_id)!=m_ActiveEvents.end()); } + uint32 Initialize(); + void StartEvent(uint16 event_id, bool overwrite = false); + void StopEvent(uint16 event_id, bool overwrite = false); + private: + void AddActiveEvent(uint16 event_id) { m_ActiveEvents.insert(event_id); } + void RemoveActiveEvent(uint16 event_id) { m_ActiveEvents.erase(event_id); } + void ApplyNewEvent(uint16 event_id); + void UnApplyEvent(uint16 event_id); + void GameEventSpawn(int16 event_id); + void GameEventUnspawn(int16 event_id); + void ChangeEquipOrModel(int16 event_id, bool activate); + void UpdateEventQuests(uint16 event_id, bool Activate); + protected: + typedef std::list GuidList; + typedef std::vector GameEventGuidMap; + typedef std::pair ModelEquipPair; + typedef std::list ModelEquipList; + typedef std::vector GameEventModelEquipMap; + typedef std::pair QuestRelation; + typedef std::list QuestRelList; + typedef std::vector GameEventQuestMap; + GameEventQuestMap mGameEventQuests; + GameEventModelEquipMap mGameEventModelEquip; + GameEventGuidMap mGameEventCreatureGuids; + GameEventGuidMap mGameEventGameobjectGuids; + GameEventDataMap mGameEvent; + ActiveEvents m_ActiveEvents; + bool isSystemInit; +}; + +#define gameeventmgr MaNGOS::Singleton::Instance() +#endif diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp new file mode 100644 index 00000000000..c3b8cf3d6e4 --- /dev/null +++ b/src/game/GameObject.cpp @@ -0,0 +1,1253 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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; +} + +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_DBTableGuid = guidlow; + 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 checker(ok, u_check); + + CellLock cell_lock(cell, p); + + TypeContainerVisitor, 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, 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 checker(p_ok, p_check); + + CellLock cell_lock(cell, p); + + TypeContainerVisitor, 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::iterator it = m_unique_users.begin(); + std::set::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 creature 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; + + // 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; + + uint32 stored_guid = 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; + + m_DBTableGuid = stored_guid; + + 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(stored_guid, 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(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 checker(trapGO,go_check); + + TypeContainerVisitor, GridTypeMapContainer > object_checker(checker); + CellLock 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 checker(ok, u_check); + + CellLock cell_lock(cell, p); + + TypeContainerVisitor, 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; ichair.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->InBattleGround() && // in battleground + !player->IsMounted() && // not mounted + !player->HasStealthAura() && // not stealthed + !player->HasInvisibilityAura() && // not invisible + player->isAlive()) // live player + { + 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->InBattleGround() && // in battleground + !player->IsMounted() && // not mounted + !player->HasStealthAura() && // not stealthed + !player->HasInvisibilityAura() && // not invisible + !player->HasAura(SPELL_RECENTLY_DROPPED_FLAG, 0) && // can't pickup + player->isAlive()) // live player + { + 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 new file mode 100644 index 00000000000..30f3a815397 --- /dev/null +++ b/src/game/GameObject.h @@ -0,0 +1,592 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef 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 Name; + std::vector 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-> +// 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::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 &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 m_SkillupList; + + std::set m_unique_users; + uint32 m_usetimes; + + uint32 m_DBTableGuid; + GameObjectInfo const* m_goInfo; + private: + void SwitchDoorOrButton(bool activate); + + GridReference m_gridRef; +}; +#endif diff --git a/src/game/GlobalEvents.cpp b/src/game/GlobalEvents.cpp new file mode 100644 index 00000000000..5bac79ad102 --- /dev/null +++ b/src/game/GlobalEvents.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup world +*/ + +#include "Log.h" +#include "Database/DatabaseEnv.h" +#include "Platform/Define.h" +#include "MapManager.h" +#include "ObjectAccessor.h" +#include "GlobalEvents.h" +#include "ObjectDefines.h" +#include "Corpse.h" + +/// Handle periodic erase of corpses and bones +static void CorpsesErase(bool bones,uint32 delay) +{ + ///- Get the list of eligible corpses/bones to be removed + //No SQL injection (uint32 and enum) + QueryResult *result = CharacterDatabase.PQuery("SELECT guid,position_x,position_y,map,player FROM corpse WHERE UNIX_TIMESTAMP()-time > '%u' AND corpse_type %s '0'", delay, (bones ? "=" : "<>") ); + + if(result) + { + do + { + Field *fields = result->Fetch(); + uint32 guidlow = fields[0].GetUInt32(); + float positionX = fields[1].GetFloat(); + float positionY = fields[2].GetFloat(); + uint32 mapid = fields[3].GetUInt32(); + uint64 player_guid = MAKE_NEW_GUID(fields[4].GetUInt32(), 0, HIGHGUID_PLAYER); + + uint64 guid = MAKE_NEW_GUID(guidlow, 0, HIGHGUID_CORPSE); + + sLog.outDebug("[Global event] Removing %s %u (X:%f Y:%f Map:%u).",(bones?"bones":"corpse"),guidlow,positionX,positionY,mapid); + + /// Resurrectable - convert corpses to bones + if(!bones) + { + if(!ObjectAccessor::Instance().ConvertCorpseForPlayer(player_guid)) + { + sLog.outDebug("Corpse %u not found in world. Delete from DB.",guidlow); + CharacterDatabase.PExecute("DELETE FROM corpse WHERE guid = '%u'",guidlow); + } + } + else + ///- or delete bones + { + MapManager::Instance().RemoveBonesFromMap(mapid, guid, positionX, positionY); + + ///- remove bones from the database + CharacterDatabase.PExecute("DELETE FROM corpse WHERE guid = '%u'",guidlow); + } + } while (result->NextRow()); + + delete result; + } +} + +/// not thread guarded variant for call from other thread +void CorpsesErase() +{ + CorpsesErase(true, 20*MINUTE); + CorpsesErase(false,3*DAY); +} diff --git a/src/game/GlobalEvents.h b/src/game/GlobalEvents.h new file mode 100644 index 00000000000..aacf72d730a --- /dev/null +++ b/src/game/GlobalEvents.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup world +/// @{ +/// \file + +#ifndef __GLOBALEVENTS_H +#define __GLOBALEVENTS_H + +void CorpsesErase(); +void HandleCorpsesErase(void*); +#endif +/// @} diff --git a/src/game/GossipDef.cpp b/src/game/GossipDef.cpp new file mode 100644 index 00000000000..57c3597a7b8 --- /dev/null +++ b/src/game/GossipDef.cpp @@ -0,0 +1,765 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 ) +{ + pGossipMenu = new GossipMenu(); + pQuestMenu = new QuestMenu(); + pSession = Session; +} + +PlayerMenu::~PlayerMenu() +{ + delete pGossipMenu; + delete pQuestMenu; +} + +void PlayerMenu::ClearMenus() +{ + pGossipMenu->ClearMenu(); + pQuestMenu->ClearMenu(); +} + +uint32 PlayerMenu::GossipOptionSender( unsigned int Selection ) +{ + return pGossipMenu->MenuItemSender( Selection ); +} + +uint32 PlayerMenu::GossipOptionAction( unsigned int Selection ) +{ + return pGossipMenu->MenuItemAction( Selection ); +} + +bool PlayerMenu::GossipOptionCoded( unsigned int Selection ) +{ + return pGossipMenu->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( pGossipMenu->MenuItemCount() ); // max count 0x0F + + for ( unsigned int iI = 0; iI < pGossipMenu->MenuItemCount(); iI++ ) + { + GossipMenuItem const& gItem = pGossipMenu->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( pQuestMenu->MenuItemCount() ); // max count 0x20 + + for ( uint16 iI = 0; iI < pQuestMenu->MenuItemCount(); iI++ ) + { + QuestMenuItem const& qItem = pQuestMenu->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 ( pQuestMenu->MenuItemCount() ); + + for ( uint16 iI = 0; iI < pQuestMenu->MenuItemCount(); iI++ ) + { + QuestMenuItem qmi=pQuestMenu->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;iObjectiveText[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;iObjectiveText[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->GetMinLevel()); // not MinLevel. Accepted values: 0, 1 or 2 Possible theory for future dev: 0==cannot in quest log, 1==can in quest log session only(removed on log out), 2==can in quest log always (save to db) + 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 new file mode 100644 index 00000000000..aacf7a8fcb8 --- /dev/null +++ b/src/game/GossipDef.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef 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 GossipMenuItemList; + +struct QuestMenuItem +{ + uint32 m_qId; + uint8 m_qIcon; +}; + +typedef std::vector 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() + { + return m_gItems.size(); + } + + 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() + { + return m_qItems.size(); + } + bool HasItem( uint32 questid ); + + QuestMenuItem const& GetItem( uint16 Id ) + { + return m_qItems[ Id ]; + } + + protected: + QuestMenuItemList m_qItems; +}; + +class MANGOS_DLL_SPEC PlayerMenu +{ + private: + GossipMenu* pGossipMenu; + QuestMenu* pQuestMenu; + WorldSession* pSession; + + public: + PlayerMenu( WorldSession *Session ); + ~PlayerMenu(); + + GossipMenu* GetGossipMenu() { return pGossipMenu; } + QuestMenu* GetQuestMenu() { return pQuestMenu; } + + 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/GridDefines.h b/src/game/GridDefines.h new file mode 100644 index 00000000000..d6287fa495c --- /dev/null +++ b/src/game/GridDefines.h @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_GRIDDEFINES_H +#define MANGOS_GRIDDEFINES_H + +#include "Common.h" +#include "GameSystem/NGrid.h" +#include + +// Forward class definitions +class Corpse; +class Creature; +class DynamicObject; +class GameObject; +class Pet; +class Player; + +#define MAX_NUMBER_OF_GRIDS 64 + +#define SIZE_OF_GRIDS 533.33333f +#define CENTER_GRID_ID (MAX_NUMBER_OF_GRIDS/2) + +#define CENTER_GRID_OFFSET (SIZE_OF_GRIDS/2) + +#define MIN_GRID_DELAY MINUTE*1000 +#define MIN_MAP_UPDATE_DELAY 50 + +#define MAX_NUMBER_OF_CELLS 8 +#define SIZE_OF_GRID_CELL (SIZE_OF_GRIDS/MAX_NUMBER_OF_CELLS) + +#define CENTER_GRID_CELL_ID 256 +#define CENTER_GRID_CELL_OFFSET (SIZE_OF_GRID_CELL/2) + +#define TOTAL_NUMBER_OF_CELLS_PER_MAP (MAX_NUMBER_OF_GRIDS*MAX_NUMBER_OF_CELLS) + +#define MAP_RESOLUTION 256 + +#define MAP_SIZE (SIZE_OF_GRIDS*MAX_NUMBER_OF_GRIDS) +#define MAP_HALFSIZE (MAP_SIZE/2) + +// Creature used instead pet to simplify *::Visit templates (not required duplicate code for Creature->Pet case) +typedef TYPELIST_3(Player, Creature/*pets*/, Corpse/*resurrectable*/) AllWorldObjectTypes; +typedef TYPELIST_4(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/) AllGridObjectTypes; + +typedef GridRefManager CorpseMapType; +typedef GridRefManager CreatureMapType; +typedef GridRefManager DynamicObjectMapType; +typedef GridRefManager GameObjectMapType; +typedef GridRefManager PlayerMapType; + +typedef Grid GridType; +typedef NGrid<8, Player, AllWorldObjectTypes, AllGridObjectTypes> NGridType; + +typedef TypeMapContainer GridTypeMapContainer; +typedef TypeMapContainer WorldTypeMapContainer; + +template +struct MANGOS_DLL_DECL CoordPair +{ + CoordPair(uint32 x=0, uint32 y=0) : x_coord(x), y_coord(y) {} + CoordPair(const CoordPair &obj) : x_coord(obj.x_coord), y_coord(obj.y_coord) {} + bool operator==(const CoordPair &obj) const { return (obj.x_coord == x_coord && obj.y_coord == y_coord); } + bool operator!=(const CoordPair &obj) const { return !operator==(obj); } + CoordPair& operator=(const CoordPair &obj) + { + x_coord = obj.x_coord; + y_coord = obj.y_coord; + return *this; + } + + void operator<<(const uint32 val) + { + if( x_coord >= val ) + x_coord -= val; + } + + void operator>>(const uint32 val) + { + if( x_coord+val < LIMIT ) + x_coord += val; + } + + void operator-=(const uint32 val) + { + if( y_coord >= val ) + y_coord -= val; + } + + void operator+=(const uint32 val) + { + if( y_coord+val < LIMIT ) + y_coord += val; + } + + uint32 x_coord; + uint32 y_coord; +}; + +typedef CoordPair GridPair; +typedef CoordPair CellPair; + +namespace MaNGOS +{ + template + inline RET_TYPE Compute(float x, float y, float center_offset, float size) + { + // calculate and store temporary values in double format for having same result as same mySQL calculations + double x_offset = (double(x) - center_offset)/size; + double y_offset = (double(y) - center_offset)/size; + + int x_val = int(x_offset+CENTER_VAL + 0.5); + int y_val = int(y_offset+CENTER_VAL + 0.5); + return RET_TYPE(x_val, y_val); + } + + inline GridPair ComputeGridPair(float x, float y) + { + return Compute(x, y, CENTER_GRID_OFFSET, SIZE_OF_GRIDS); + } + + inline CellPair ComputeCellPair(float x, float y) + { + return Compute(x, y, CENTER_GRID_CELL_OFFSET, SIZE_OF_GRID_CELL); + } + + inline void NormalizeMapCoord(float &c) + { + if(c > MAP_HALFSIZE - 0.5) + c = MAP_HALFSIZE - 0.5; + else if(c < -(MAP_HALFSIZE - 0.5)) + c = -(MAP_HALFSIZE - 0.5); + } + + inline bool IsValidMapCoord(float c) + { + return finite(c) && (std::fabs(c) <= MAP_HALFSIZE - 0.5); + } + + inline bool IsValidMapCoord(float x, float y) + { + return IsValidMapCoord(x) && IsValidMapCoord(y); + } + + inline bool IsValidMapCoord(float x, float y, float z) + { + return IsValidMapCoord(x,y) && finite(z); + } + + inline bool IsValidMapCoord(float x, float y, float z, float o) + { + return IsValidMapCoord(x,y,z) && finite(o); + } +} +#endif diff --git a/src/game/GridNotifiers.cpp b/src/game/GridNotifiers.cpp new file mode 100644 index 00000000000..5348b7f61b0 --- /dev/null +++ b/src/game/GridNotifiers.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "GridNotifiers.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "UpdateData.h" +#include "Item.h" +#include "Map.h" +#include "MapManager.h" +#include "Transports.h" +#include "ObjectAccessor.h" + +using namespace MaNGOS; + +void +MaNGOS::PlayerNotifier::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + if( iter->getSource() == &i_player ) + continue; + + iter->getSource()->UpdateVisibilityOf(&i_player); + i_player.UpdateVisibilityOf(iter->getSource()); + } +} + +void +VisibleChangesNotifier::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + if(iter->getSource() == &i_object) + continue; + + iter->getSource()->UpdateVisibilityOf(&i_object); + } +} + +void +VisibleNotifier::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + if( iter->getSource() == &i_player ) + continue; + + iter->getSource()->UpdateVisibilityOf(&i_player); + i_player.UpdateVisibilityOf(iter->getSource(),i_data,i_data_updates,i_visibleNow); + i_clientGUIDs.erase(iter->getSource()->GetGUID()); + } +} + +void +VisibleNotifier::Notify() +{ + // at this moment i_clientGUIDs have guids that not iterate at grid level checks + // but exist one case when this possible and object not out of range: transports + if(Transport* transport = i_player.GetTransport()) + { + for(Transport::PlayerSet::const_iterator itr = transport->GetPassengers().begin();itr!=transport->GetPassengers().end();++itr) + { + if(i_clientGUIDs.find((*itr)->GetGUID())!=i_clientGUIDs.end()) + { + (*itr)->UpdateVisibilityOf(&i_player); + i_player.UpdateVisibilityOf((*itr),i_data,i_data_updates,i_visibleNow); + i_clientGUIDs.erase((*itr)->GetGUID()); + } + } + } + + // generate outOfRange for not iterate objects + i_data.AddOutOfRangeGUID(i_clientGUIDs); + for(Player::ClientGUIDs::iterator itr = i_clientGUIDs.begin();itr!=i_clientGUIDs.end();++itr) + { + i_player.m_clientGUIDs.erase(*itr); + + #ifdef MANGOS_DEBUG + if((sLog.getLogFilter() & LOG_FILTER_VISIBILITY_CHANGES)==0) + sLog.outDebug("Object %u (Type: %u) is out of range (no in active cells set) now for player %u",GUID_LOPART(*itr),GuidHigh2TypeId(GUID_HIPART(*itr)),i_player.GetGUIDLow()); + #endif + } + + // send update to other players (except player updates that already sent using SendUpdateToPlayer) + for(UpdateDataMapType::iterator iter = i_data_updates.begin(); iter != i_data_updates.end(); ++iter) + { + if(iter->first==&i_player) + continue; + + WorldPacket packet; + iter->second.BuildPacket(&packet); + iter->first->GetSession()->SendPacket(&packet); + } + + if( i_data.HasData() ) + { + // send create/outofrange packet to player (except player create updates that already sent using SendUpdateToPlayer) + WorldPacket packet; + i_data.BuildPacket(&packet); + i_player.GetSession()->SendPacket(&packet); + + // send out of range to other players if need + std::set const& oor = i_data.GetOutOfRangeGUIDs(); + for(std::set::const_iterator iter = oor.begin(); iter != oor.end(); ++iter) + { + if(!IS_PLAYER_GUID(*iter)) + continue; + + Player* plr = ObjectAccessor::GetPlayer(i_player,*iter); + if(plr) + plr->UpdateVisibilityOf(&i_player); + } + } + + // Now do operations that required done at object visibility change to visible + + // target aura duration for caster show only if target exist at caster client + // send data at target visibility change (adding to client) + for(std::set::const_iterator vItr = i_visibleNow.begin(); vItr != i_visibleNow.end(); ++vItr) + if((*vItr)!=&i_player && (*vItr)->isType(TYPEMASK_UNIT)) + i_player.SendAuraDurationsForTarget((Unit*)(*vItr)); +} + +void +MessageDeliverer::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + if( i_toSelf || iter->getSource() != &i_player) + { + if(WorldSession* session = iter->getSource()->GetSession()) + session->SendPacket(i_message); + } + } +} + +void +ObjectMessageDeliverer::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + if(WorldSession* session = iter->getSource()->GetSession()) + session->SendPacket(i_message); + } +} + +void +MessageDistDeliverer::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + if( (i_toSelf || iter->getSource() != &i_player ) && + (!i_ownTeamOnly || iter->getSource()->GetTeam() == i_player.GetTeam() ) && + (!i_dist || iter->getSource()->GetDistance(&i_player) <= i_dist) ) + { + if(WorldSession* session = iter->getSource()->GetSession()) + session->SendPacket(i_message); + } + } +} + +void +ObjectMessageDistDeliverer::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + if( !i_dist || iter->getSource()->GetDistance(&i_object) <= i_dist ) + { + if(WorldSession* session = iter->getSource()->GetSession()) + session->SendPacket(i_message); + } + } +} + +template void +ObjectUpdater::Visit(GridRefManager &m) +{ + for(typename GridRefManager::iterator iter = m.begin(); iter != m.end(); ++iter) + { + iter->getSource()->Update(i_timeDiff); + } +} + +template void ObjectUpdater::Visit(GameObjectMapType &); +template void ObjectUpdater::Visit(DynamicObjectMapType &); + +bool CannibalizeObjectCheck::operator()(Corpse* u) +{ + // ignore bones + if(u->GetType()==CORPSE_BONES) + return false; + + Player* owner = ObjectAccessor::FindPlayer(u->GetOwnerGUID()); + + if( !owner || i_funit->IsFriendlyTo(owner)) + return false; + + if(i_funit->IsWithinDistInMap(u, i_range) ) + return true; + + return false; +} diff --git a/src/game/GridNotifiers.h b/src/game/GridNotifiers.h new file mode 100644 index 00000000000..215cf9152e6 --- /dev/null +++ b/src/game/GridNotifiers.h @@ -0,0 +1,812 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_GRIDNOTIFIERS_H +#define MANGOS_GRIDNOTIFIERS_H + +#include "ObjectGridLoader.h" +#include "ByteBuffer.h" +#include "UpdateData.h" +#include + +#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 void Visit(GridRefManager &) {} + Player &i_player; + }; + + struct MANGOS_DLL_DECL VisibleNotifier + { + Player &i_player; + UpdateData i_data; + UpdateDataMapType i_data_updates; + Player::ClientGUIDs i_clientGUIDs; + std::set i_visibleNow; + + explicit VisibleNotifier(Player &player) : i_player(player),i_clientGUIDs(player.m_clientGUIDs) {} + template void Visit(GridRefManager &m); + void Visit(PlayerMapType &); + void Notify(void); + }; + + struct MANGOS_DLL_DECL VisibleChangesNotifier + { + WorldObject &i_object; + + explicit VisibleChangesNotifier(WorldObject &object) : i_object(object) {} + template void Visit(GridRefManager &) {} + 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 void updateObjects(GridRefManager &m) + { + for(typename GridRefManager::iterator iter = m.begin(); iter != m.end(); ++iter) + iter->getSource()->Update(i_timeDiff); + } + + void Visit(PlayerMapType &m) { updateObjects(m); } + void Visit(CreatureMapType &m){ updateObjects(m); } + void Visit(GameObjectMapType &m) { updateObjects(m); } + void Visit(DynamicObjectMapType &m) { updateObjects(m); } + void Visit(CorpseMapType &m) { updateObjects(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 void Visit(GridRefManager &) {} + }; + + struct MANGOS_DLL_DECL ObjectMessageDeliverer + { + WorldPacket *i_message; + explicit ObjectMessageDeliverer(WorldPacket *msg) : i_message(msg) {} + void Visit(PlayerMapType &m); + template void Visit(GridRefManager &) {} + }; + + 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, bool to_self, bool ownTeamOnly, float dist) : i_player(pl), i_message(msg), i_toSelf(to_self), i_ownTeamOnly(ownTeamOnly), i_dist(dist) {} + void Visit(PlayerMapType &m); + template void Visit(GridRefManager &) {} + }; + + 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 void Visit(GridRefManager &) {} + }; + + struct MANGOS_DLL_DECL ObjectUpdater + { + uint32 i_timeDiff; + explicit ObjectUpdater(const uint32 &diff) : i_timeDiff(diff) {} + template void Visit(GridRefManager &m); + void Visit(PlayerMapType &) {} + void Visit(CorpseMapType &) {} + void Visit(CreatureMapType &); + }; + + template + 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 &m ) + { + if( i_object == NULL ) + { + GridRefManager *iter = m.find(i_id); + if( iter != m.end() ) + { + assert( iter->second != NULL ); + i_object = iter->second; + } + } + } + + template void Visit(GridRefManager &) {} + }; + + struct MANGOS_DLL_DECL PlayerRelocationNotifier + { + Player &i_player; + PlayerRelocationNotifier(Player &pl) : i_player(pl) {} + template void Visit(GridRefManager &) {} + void Visit(PlayerMapType &); + void Visit(CreatureMapType &); + }; + + struct MANGOS_DLL_DECL CreatureRelocationNotifier + { + Creature &i_creature; + CreatureRelocationNotifier(Creature &c) : i_creature(c) {} + template void Visit(GridRefManager &) {} + #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 inline void Visit(GridRefManager &) {} + #ifdef WIN32 + template<> inline void Visit(PlayerMapType &); + template<> inline void Visit(CreatureMapType &); + #endif + + void VisitHelper(Unit* target); + }; + + // SEARCHERS & LIST SEARCHERS & WORKERS + + // WorldObject searchers & workers + + template + 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 void Visit(GridRefManager &) {} + }; + + template + struct MANGOS_DLL_DECL WorldObjectListSearcher + { + std::list &i_objects; + Check& i_check; + + WorldObjectListSearcher(std::list &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 void Visit(GridRefManager &) {} + }; + + template + 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 void Visit(GridRefManager &) {} + }; + + // Gameobject searchers + + template + 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 void Visit(GridRefManager &) {} + }; + + // Last accepted by Check GO if any (Check can change requirements at each call) + template + 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 void Visit(GridRefManager &) {} + }; + + template + struct MANGOS_DLL_DECL GameObjectListSearcher + { + std::list &i_objects; + Check& i_check; + + GameObjectListSearcher(std::list &objects, Check & check) : i_objects(objects),i_check(check) {} + + void Visit(GameObjectMapType &m); + + template void Visit(GridRefManager &) {} + }; + + // Unit searchers + + // First accepted by Check Unit if any + template + 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 void Visit(GridRefManager &) {} + }; + + // Last accepted by Check Unit if any (Check can change requirements at each call) + template + 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 void Visit(GridRefManager &) {} + }; + + // All accepted by Check units if any + template + struct MANGOS_DLL_DECL UnitListSearcher + { + std::list &i_objects; + Check& i_check; + + UnitListSearcher(std::list &objects, Check & check) : i_objects(objects),i_check(check) {} + + void Visit(PlayerMapType &m); + void Visit(CreatureMapType &m); + + template void Visit(GridRefManager &) {} + }; + + // Creature searchers + + template + 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 void Visit(GridRefManager &) {} + }; + + // Last accepted by Check Creature if any (Check can change requirements at each call) + template + 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 void Visit(GridRefManager &) {} + }; + + template + struct MANGOS_DLL_DECL CreatureListSearcher + { + std::list &i_objects; + Check& i_check; + + CreatureListSearcher(std::list &objects, Check & check) : i_objects(objects),i_check(check) {} + + void Visit(CreatureMapType &m); + + template void Visit(GridRefManager &) {} + }; + + // Player searchers + + template + 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 void Visit(GridRefManager &) {} + }; + + template + 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 void Visit(GridRefManager &) {} + }; + + // 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 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 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; + }; + + #ifndef WIN32 + template<> void PlayerRelocationNotifier::Visit(CreatureMapType &); + template<> void PlayerRelocationNotifier::Visit(PlayerMapType &); + template<> void CreatureRelocationNotifier::Visit(PlayerMapType &); + template<> void CreatureRelocationNotifier::Visit(CreatureMapType &); + template<> inline void DynamicObjectUpdater::Visit(CreatureMapType &); + template<> inline void DynamicObjectUpdater::Visit(PlayerMapType &); + #endif +} +#endif diff --git a/src/game/GridNotifiersImpl.h b/src/game/GridNotifiersImpl.h new file mode 100644 index 00000000000..9dd422a3ad3 --- /dev/null +++ b/src/game/GridNotifiersImpl.h @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_GRIDNOTIFIERSIMPL_H +#define MANGOS_GRIDNOTIFIERSIMPL_H + +#include "GridNotifiers.h" +#include "WorldPacket.h" +#include "Corpse.h" +#include "Player.h" +#include "UpdateData.h" +#include "CreatureAI.h" +#include "SpellAuras.h" + +template +inline void +MaNGOS::VisibleNotifier::Visit(GridRefManager &m) +{ + for(typename GridRefManager::iterator iter = m.begin(); iter != m.end(); ++iter) + { + i_player.UpdateVisibilityOf(iter->getSource(),i_data,i_data_updates,i_visibleNow); + i_clientGUIDs.erase(iter->getSource()->GetGUID()); + } +} + +inline void +MaNGOS::ObjectUpdater::Visit(CreatureMapType &m) +{ + for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + if(!iter->getSource()->isSpiritService()) + iter->getSource()->Update(i_timeDiff); +} + +inline void +MaNGOS::PlayerRelocationNotifier::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + if(&i_player==iter->getSource()) + continue; + + // visibility for players updated by ObjectAccessor::UpdateVisibilityFor calls in appropriate places + + // Cancel Trade + if(i_player.GetTrader()==iter->getSource()) + // iteraction distance + if(!i_player.IsWithinDistInMap(iter->getSource(), 5)) + i_player.GetSession()->SendCancelTrade(); // will clode both side trade windows + } +} + +inline void PlayerCreatureRelocationWorker(Player* pl, Creature* c) +{ + // update creature visibility at player/creature move + pl->UpdateVisibilityOf(c); + + // Creature AI reaction + if(!c->hasUnitState(UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING)) + { + if( c->AI() && c->AI()->IsVisible(pl) && !c->IsInEvadeMode() ) + c->AI()->MoveInLineOfSight(pl); + } +} + +inline void CreatureCreatureRelocationWorker(Creature* c1, Creature* c2) +{ + if(!c1->hasUnitState(UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING)) + { + if( c1->AI() && c1->AI()->IsVisible(c2) && !c1->IsInEvadeMode() ) + c1->AI()->MoveInLineOfSight(c2); + } + + if(!c2->hasUnitState(UNIT_STAT_CHASE | UNIT_STAT_SEARCHING | UNIT_STAT_FLEEING)) + { + if( c2->AI() && c2->AI()->IsVisible(c1) && !c2->IsInEvadeMode() ) + c2->AI()->MoveInLineOfSight(c1); + } +} + +inline void +MaNGOS::PlayerRelocationNotifier::Visit(CreatureMapType &m) +{ + if(!i_player.isAlive() || i_player.isInFlight()) + return; + + for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + if( iter->getSource()->isAlive()) + PlayerCreatureRelocationWorker(&i_player,iter->getSource()); +} + +template<> +inline void +MaNGOS::CreatureRelocationNotifier::Visit(PlayerMapType &m) +{ + if(!i_creature.isAlive()) + return; + + for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + if( iter->getSource()->isAlive() && !iter->getSource()->isInFlight()) + PlayerCreatureRelocationWorker(iter->getSource(), &i_creature); +} + +template<> +inline void +MaNGOS::CreatureRelocationNotifier::Visit(CreatureMapType &m) +{ + if(!i_creature.isAlive()) + return; + + for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + Creature* c = iter->getSource(); + if( c != &i_creature && c->isAlive()) + CreatureCreatureRelocationWorker(c, &i_creature); + } +} + +inline void MaNGOS::DynamicObjectUpdater::VisitHelper(Unit* target) +{ + if(!target->isAlive() || target->isInFlight() ) + return; + + if(target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->isTotem()) + return; + + if (!i_dynobject.IsWithinDistInMap(target, i_dynobject.GetRadius())) + return; + + //Check targets for not_selectable unit flag and remove + if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE)) + return; + + // Evade target + if( target->GetTypeId()==TYPEID_UNIT && ((Creature*)target)->IsInEvadeMode() ) + return; + + //Check player targets and remove if in GM mode or GM invisibility (for not self casting case) + if( target->GetTypeId()==TYPEID_PLAYER && target != i_check && (((Player*)target)->isGameMaster() || ((Player*)target)->GetVisibility()==VISIBILITY_OFF) ) + return; + + if( i_check->GetTypeId()==TYPEID_PLAYER ) + { + if (i_check->IsFriendlyTo( target )) + return; + } + else + { + if (!i_check->IsHostileTo( target )) + return; + } + + if (i_dynobject.IsAffecting(target)) + return; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(i_dynobject.GetSpellId()); + uint32 eff_index = i_dynobject.GetEffIndex(); + // Check target immune to spell or aura + if (target->IsImmunedToSpell(spellInfo) || target->IsImmunedToSpellEffect(spellInfo->Effect[eff_index], spellInfo->EffectMechanic[eff_index])) + return; + // Apply PersistentAreaAura on target + PersistentAreaAura* Aur = new PersistentAreaAura(spellInfo, eff_index, NULL, target, i_dynobject.GetCaster()); + target->AddAura(Aur); + i_dynobject.AddAffected(target); +} + +template<> +inline void +MaNGOS::DynamicObjectUpdater::Visit(CreatureMapType &m) +{ + for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + VisitHelper(itr->getSource()); +} + +template<> +inline void +MaNGOS::DynamicObjectUpdater::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + VisitHelper(itr->getSource()); +} + +// SEARCHERS & LIST SEARCHERS & WORKERS + +// WorldObject searchers & workers + +template +void MaNGOS::WorldObjectSearcher::Visit(GameObjectMapType &m) +{ + // already found + if(i_object) + return; + + for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + { + i_object = itr->getSource(); + return; + } + } +} + +template +void MaNGOS::WorldObjectSearcher::Visit(PlayerMapType &m) +{ + // already found + if(i_object) + return; + + for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + { + i_object = itr->getSource(); + return; + } + } +} + +template +void MaNGOS::WorldObjectSearcher::Visit(CreatureMapType &m) +{ + // already found + if(i_object) + return; + + for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + { + i_object = itr->getSource(); + return; + } + } +} + +template +void MaNGOS::WorldObjectSearcher::Visit(CorpseMapType &m) +{ + // already found + if(i_object) + return; + + for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + { + i_object = itr->getSource(); + return; + } + } +} + +template +void MaNGOS::WorldObjectSearcher::Visit(DynamicObjectMapType &m) +{ + // already found + if(i_object) + return; + + for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + { + i_object = itr->getSource(); + return; + } + } +} + +template +void MaNGOS::WorldObjectListSearcher::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); +} + +template +void MaNGOS::WorldObjectListSearcher::Visit(CreatureMapType &m) +{ + for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); +} + +template +void MaNGOS::WorldObjectListSearcher::Visit(CorpseMapType &m) +{ + for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); +} + +template +void MaNGOS::WorldObjectListSearcher::Visit(GameObjectMapType &m) +{ + for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); +} + +template +void MaNGOS::WorldObjectListSearcher::Visit(DynamicObjectMapType &m) +{ + for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); +} + +// Gameobject searchers + +template +void MaNGOS::GameObjectSearcher::Visit(GameObjectMapType &m) +{ + // already found + if(i_object) + return; + + for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + { + i_object = itr->getSource(); + return; + } + } +} + +template +void MaNGOS::GameObjectLastSearcher::Visit(GameObjectMapType &m) +{ + for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + i_object = itr->getSource(); + } +} + +template +void MaNGOS::GameObjectListSearcher::Visit(GameObjectMapType &m) +{ + for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); +} + +// Unit searchers + +template +void MaNGOS::UnitSearcher::Visit(CreatureMapType &m) +{ + // already found + if(i_object) + return; + + for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + { + i_object = itr->getSource(); + return; + } + } +} + +template +void MaNGOS::UnitSearcher::Visit(PlayerMapType &m) +{ + // already found + if(i_object) + return; + + for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + { + i_object = itr->getSource(); + return; + } + } +} + +template +void MaNGOS::UnitLastSearcher::Visit(CreatureMapType &m) +{ + for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + i_object = itr->getSource(); + } +} + +template +void MaNGOS::UnitLastSearcher::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + i_object = itr->getSource(); + } +} + +template +void MaNGOS::UnitListSearcher::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); +} + +template +void MaNGOS::UnitListSearcher::Visit(CreatureMapType &m) +{ + for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); +} + +// Creature searchers + +template +void MaNGOS::CreatureSearcher::Visit(CreatureMapType &m) +{ + // already found + if(i_object) + return; + + for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + { + i_object = itr->getSource(); + return; + } + } +} + +template +void MaNGOS::CreatureLastSearcher::Visit(CreatureMapType &m) +{ + for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + i_object = itr->getSource(); + } +} + +template +void MaNGOS::CreatureListSearcher::Visit(CreatureMapType &m) +{ + for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + if(i_check(itr->getSource())) + i_objects.push_back(itr->getSource()); +} + +template +void MaNGOS::PlayerSearcher::Visit(PlayerMapType &m) +{ + // already found + if(i_object) + return; + + for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if(i_check(itr->getSource())) + { + i_object = itr->getSource(); + return; + } + } +} + +#endif // MANGOS_GRIDNOTIFIERSIMPL_H diff --git a/src/game/GridStates.cpp b/src/game/GridStates.cpp new file mode 100644 index 00000000000..0c4557fb131 --- /dev/null +++ b/src/game/GridStates.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "GridStates.h" +#include "GridNotifiers.h" +#include "ObjectAccessor.h" +#include "GameSystem/Grid.h" +#include "Log.h" + +void +InvalidState::Update(Map &, NGridType &, GridInfo &, const uint32 &/*x*/, const uint32 &/*y*/, const uint32 &) const +{ +} + +void +ActiveState::Update(Map &m, NGridType &grid, GridInfo & info, const uint32 &x, const uint32 &y, const uint32 &t_diff) const +{ + // Only check grid activity every (grid_expiry/10) ms, because it's really useless to do it every cycle + info.UpdateTimeTracker(t_diff); + if( info.getTimeTracker().Passed() ) + { + if( grid.ActiveObjectsInGrid() == 0 && !ObjectAccessor::Instance().PlayersNearGrid(x, y, m.GetId(), m.GetInstanceId()) ) + { + ObjectGridStoper stoper(grid); + stoper.StopN(); + grid.SetGridState(GRID_STATE_IDLE); + } + else + { + m.ResetGridExpiry(grid, 0.1f); + } + } +} + +void +IdleState::Update(Map &m, NGridType &grid, GridInfo &info, const uint32 &x, const uint32 &y, const uint32 &) const +{ + m.ResetGridExpiry(grid); + grid.SetGridState(GRID_STATE_REMOVAL); + sLog.outDebug("Grid[%u,%u] on map %u moved to IDLE state", x, y, m.GetId()); +} + +void +RemovalState::Update(Map &m, NGridType &grid, GridInfo &info, const uint32 &x, const uint32 &y, const uint32 &t_diff) const +{ + if(info.getUnloadFlag()) + { + info.UpdateTimeTracker(t_diff); + if( info.getTimeTracker().Passed() ) + { + if( !m.UnloadGrid(x, y, false) ) + { + sLog.outDebug("Grid[%u,%u] for map %u differed unloading due to players nearby", x, y, m.GetId()); + m.ResetGridExpiry(grid); + } + } + } +} diff --git a/src/game/GridStates.h b/src/game/GridStates.h new file mode 100644 index 00000000000..8a4e716ece4 --- /dev/null +++ b/src/game/GridStates.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_GRIDSTATES_H +#define MANGOS_GRIDSTATES_H + +#include "Map.h" + +class MANGOS_DLL_DECL GridState +{ + public: +#ifdef MANGOS_DEBUG +#define MAGIC_TESTVAL 0xFBE823BA + GridState() { i_Magic = MAGIC_TESTVAL; } + bool checkMagic() + { + if(i_Magic != MAGIC_TESTVAL) + { + sLog.outError("!!! GridState: Magic value gone !!!"); + return false; + } + return true; + } + void setMagic() { i_Magic = MAGIC_TESTVAL; } + unsigned int i_Magic; +#endif + virtual void Update(Map &, NGridType&, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const = 0; +}; + +class MANGOS_DLL_DECL InvalidState : public GridState +{ + public: + + void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const; +}; + +class MANGOS_DLL_DECL ActiveState : public GridState +{ + public: + + void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const; +}; + +class MANGOS_DLL_DECL IdleState : public GridState +{ + public: + + void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const; +}; + +class MANGOS_DLL_DECL RemovalState : public GridState +{ + public: + + void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const; +}; +#endif diff --git a/src/game/Group.cpp b/src/game/Group.cpp new file mode 100644 index 00000000000..e74a3778e87 --- /dev/null +++ b/src/game/Group.cpp @@ -0,0 +1,1406 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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; iGetBgRaid(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; iNextRow() ); + 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::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::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(0,real_count); + + for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *looter = itr->getSource(); + if (looter->GetDistance2d(creature) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)) + looter->GetSession()->SendPacket(&data); + } +} + +void Group::CountRollVote(uint64 playerGUID, uint64 Guid, uint32 NumberOfPlayers, uint8 Choise) +{ + Rolls::iterator rollI = GetRoll(Guid); + if (rollI == RollId.end()) + return; + Roll* roll = *rollI; + + Roll::PlayerVote::iterator itr = roll->playerVote.find(playerGUID); + // this condition means that player joins to the party after roll begins + if (itr == roll->playerVote.end()) + return; + + if (roll->getLoot()) + if (roll->getLoot()->items.empty()) + return; + + switch (Choise) + { + case 0: //Player choose pass + { + SendLootRoll(0, playerGUID, 128, 128, *roll); + ++roll->totalPass; + itr->second = PASS; + } + break; + case 1: //player choose Need + { + SendLootRoll(0, playerGUID, 1, 1, *roll); + ++roll->totalNeed; + itr->second = NEED; + } + break; + case 2: //player choose Greed + { + SendLootRoll(0, playerGUID, 2, 2, *roll); + ++roll->totalGreed; + itr->second = GREED; + } + break; + } + if (roll->totalPass + roll->totalGreed + roll->totalNeed >= roll->totalPlayersRolling) + { + CountTheRoll(rollI, NumberOfPlayers); + } +} + +//called when roll timer expires +void Group::EndRoll() +{ + Rolls::iterator itr; + while(!RollId.empty()) + { + //need more testing here, if rolls disappear + itr = RollId.begin(); + CountTheRoll(itr, GetMembersCount()); //i don't have to edit player votes, who didn't vote ... he will pass + } +} + +void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers) +{ + Roll* roll = *rollI; + if(!roll->isValid()) // is loot already deleted ? + { + RollId.erase(rollI); + delete roll; + return; + } + //end of the roll + if (roll->totalNeed > 0) + { + if(!roll->playerVote.empty()) + { + uint8 maxresul = 0; + uint64 maxguid = (*roll->playerVote.begin()).first; + Player *player; + + for( Roll::PlayerVote::const_iterator itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr) + { + if (itr->second != NEED) + continue; + + uint8 randomN = urand(1, 99); + SendLootRoll(0, itr->first, randomN, 1, *roll); + if (maxresul < randomN) + { + maxguid = itr->first; + maxresul = randomN; + } + } + SendLootRollWon(0, maxguid, maxresul, 1, *roll); + player = objmgr.GetPlayer(maxguid); + + if(player && player->GetSession()) + { + ItemPosCountVec dest; + LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); + uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); + if ( msg == EQUIP_ERR_OK ) + { + item->is_looted = true; + roll->getLoot()->NotifyItemRemoved(roll->itemSlot); + --roll->getLoot()->unlootedCount; + player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId); + } + else + { + item->is_blocked = false; + player->SendEquipError( msg, NULL, NULL ); + } + } + } + } + else if (roll->totalGreed > 0) + { + if(!roll->playerVote.empty()) + { + uint8 maxresul = 0; + uint64 maxguid = (*roll->playerVote.begin()).first; + Player *player; + + Roll::PlayerVote::iterator itr; + for (itr=roll->playerVote.begin(); itr!=roll->playerVote.end(); ++itr) + { + if (itr->second != GREED) + continue; + + uint8 randomN = urand(1, 99); + SendLootRoll(0, itr->first, randomN, 2, *roll); + if (maxresul < randomN) + { + maxguid = itr->first; + maxresul = randomN; + } + } + SendLootRollWon(0, maxguid, maxresul, 2, *roll); + player = objmgr.GetPlayer(maxguid); + + if(player && player->GetSession()) + { + ItemPosCountVec dest; + LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); + uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count ); + if ( msg == EQUIP_ERR_OK ) + { + item->is_looted = true; + roll->getLoot()->NotifyItemRemoved(roll->itemSlot); + --roll->getLoot()->unlootedCount; + player->StoreNewItem( dest, roll->itemid, true, item->randomPropertyId); + } + else + { + item->is_blocked = false; + player->SendEquipError( msg, NULL, NULL ); + } + } + } + } + else + { + SendLootAllPassed(NumberOfPlayers, *roll); + LootItem *item = &(roll->getLoot()->items[roll->itemSlot]); + if(item) item->is_blocked = false; + } + RollId.erase(rollI); + delete roll; +} + +void Group::SetTargetIcon(uint8 id, uint64 guid) +{ + if(id >= TARGETICONCOUNT) + return; + + // clean other icons + if( guid != 0 ) + for(int i=0; inext()) + { + 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; iSendPacket(&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 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; iSetGroup(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(); +} + +//=================================================== +//============== 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/Group.h b/src/game/Group.h new file mode 100644 index 00000000000..14ea3ad7cba --- /dev/null +++ b/src/game/Group.h @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_GROUP_H +#define MANGOSSERVER_GROUP_H + +#include "GroupReference.h" +#include "GroupRefManager.h" +#include "LootMgr.h" + +#include +#include + +#define MAXGROUPSIZE 5 +#define MAXRAIDSIZE 40 +#define TARGETICONCOUNT 8 + +enum RollVote +{ + PASS = 0, + NEED = 1, + GREED = 2, + NOT_EMITED_YET = 3, + NOT_VALID = 4 +}; + +enum GroupMemberOnlineStatus +{ + MEMBER_STATUS_OFFLINE = 0x0000, + MEMBER_STATUS_ONLINE = 0x0001, + MEMBER_STATUS_PVP = 0x0002, + MEMBER_STATUS_UNK0 = 0x0004, // dead? (health=0) + MEMBER_STATUS_UNK1 = 0x0008, // ghost? (health=1) + MEMBER_STATUS_UNK2 = 0x0010, // never seen + MEMBER_STATUS_UNK3 = 0x0020, // never seen + MEMBER_STATUS_UNK4 = 0x0040, // appears with dead and ghost flags + MEMBER_STATUS_UNK5 = 0x0080, // never seen +}; + +enum GroupType +{ + GROUPTYPE_NORMAL = 0, + GROUPTYPE_RAID = 1 +}; + +class BattleGround; + +enum GroupUpdateFlags +{ + GROUP_UPDATE_FLAG_NONE = 0x00000000, // nothing + GROUP_UPDATE_FLAG_STATUS = 0x00000001, // uint16, flags + GROUP_UPDATE_FLAG_CUR_HP = 0x00000002, // uint16 + GROUP_UPDATE_FLAG_MAX_HP = 0x00000004, // uint16 + GROUP_UPDATE_FLAG_POWER_TYPE = 0x00000008, // uint8 + GROUP_UPDATE_FLAG_CUR_POWER = 0x00000010, // uint16 + GROUP_UPDATE_FLAG_MAX_POWER = 0x00000020, // uint16 + GROUP_UPDATE_FLAG_LEVEL = 0x00000040, // uint16 + GROUP_UPDATE_FLAG_ZONE = 0x00000080, // uint16 + GROUP_UPDATE_FLAG_POSITION = 0x00000100, // uint16, uint16 + GROUP_UPDATE_FLAG_AURAS = 0x00000200, // uint64 mask, for each bit set uint16 spellid + uint8 unk + GROUP_UPDATE_FLAG_PET_GUID = 0x00000400, // uint64 pet guid + GROUP_UPDATE_FLAG_PET_NAME = 0x00000800, // pet name, NULL terminated string + GROUP_UPDATE_FLAG_PET_MODEL_ID = 0x00001000, // uint16, model id + GROUP_UPDATE_FLAG_PET_CUR_HP = 0x00002000, // uint16 pet cur health + GROUP_UPDATE_FLAG_PET_MAX_HP = 0x00004000, // uint16 pet max health + GROUP_UPDATE_FLAG_PET_POWER_TYPE = 0x00008000, // uint8 pet power type + GROUP_UPDATE_FLAG_PET_CUR_POWER = 0x00010000, // uint16 pet cur power + GROUP_UPDATE_FLAG_PET_MAX_POWER = 0x00020000, // uint16 pet max power + GROUP_UPDATE_FLAG_PET_AURAS = 0x00040000, // uint64 mask, for each bit set uint16 spellid + uint8 unk, pet auras... + GROUP_UPDATE_PET = 0x0007FC00, // all pet flags + GROUP_UPDATE_FULL = 0x0007FFFF, // all known flags +}; + +#define GROUP_UPDATE_FLAGS_COUNT 20 + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,16,17,18,19 +static const uint8 GroupUpdateLength[GROUP_UPDATE_FLAGS_COUNT] = { 0, 2, 2, 2, 1, 2, 2, 2, 2, 4, 8, 8, 1, 2, 2, 2, 1, 2, 2, 8}; + +class InstanceSave; + +class Roll : public LootValidatorRef +{ + public: + Roll(uint64 _guid, LootItem const& li) + : itemGUID(_guid), itemid(li.itemid), itemRandomPropId(li.randomPropertyId), itemRandomSuffix(li.randomSuffix), + totalPlayersRolling(0), totalNeed(0), totalGreed(0), totalPass(0), itemSlot(0) {} + ~Roll() { } + void setLoot(Loot *pLoot) { link(pLoot, this); } + Loot *getLoot() { return getTarget(); } + void targetObjectBuildLink(); + + uint64 itemGUID; + uint32 itemid; + int32 itemRandomPropId; + uint32 itemRandomSuffix; + typedef std::map PlayerVote; + PlayerVote playerVote; //vote position correspond with player position (in group) + uint8 totalPlayersRolling; + uint8 totalNeed; + uint8 totalGreed; + uint8 totalPass; + uint8 itemSlot; +}; + +struct InstanceGroupBind +{ + InstanceSave *save; + bool perm; + /* permanent InstanceGroupBinds exist iff the leader has a permanent + PlayerInstanceBind for the same instance. */ + InstanceGroupBind() : save(NULL), perm(false) {} +}; + +/** request member stats checken **/ +/** todo: uninvite people that not accepted invite **/ +class MANGOS_DLL_SPEC Group +{ + public: + struct MemberSlot + { + uint64 guid; + std::string name; + uint8 group; + bool assistant; + }; + typedef std::list MemberSlotList; + typedef MemberSlotList::const_iterator member_citerator; + + typedef HM_NAMESPACE::hash_map< uint32 /*mapId*/, InstanceGroupBind> BoundInstancesMap; + protected: + typedef MemberSlotList::iterator member_witerator; + typedef std::set InvitesList; + + typedef std::vector Rolls; + + public: + Group(); + ~Group(); + + // group manipulation methods + bool Create(const uint64 &guid, const char * name); + bool LoadGroupFromDB(const uint64 &leaderGuid, QueryResult *result = NULL, bool loadMembers = true); + bool LoadMemberFromDB(uint32 guidLow, uint8 subgroup, bool assistant); + bool AddInvite(Player *player); + uint32 RemoveInvite(Player *player); + void RemoveAllInvites(); + bool AddLeaderInvite(Player *player); + bool AddMember(const uint64 &guid, const char* name); + // method: 0=just remove, 1=kick + uint32 RemoveMember(const uint64 &guid, const uint8 &method); + void ChangeLeader(const uint64 &guid); + void SetLootMethod(LootMethod method) { m_lootMethod = method; } + void SetLooterGuid(const uint64 &guid) { m_looterGuid = guid; } + void UpdateLooterGuid( Creature* creature, bool ifneed = false ); + void SetLootThreshold(ItemQualities threshold) { m_lootThreshold = threshold; } + void Disband(bool hideDestroy=false); + + // properties accessories + bool IsFull() const { return (m_groupType==GROUPTYPE_NORMAL) ? (m_memberSlots.size()>=MAXGROUPSIZE) : (m_memberSlots.size()>=MAXRAIDSIZE); } + bool isRaidGroup() const { return m_groupType==GROUPTYPE_RAID; } + bool isBGGroup() const { return m_bgGroup != NULL; } + bool IsCreated() const { return GetMembersCount() > 0; } + const uint64& GetLeaderGUID() const { return m_leaderGuid; } + const char * GetLeaderName() const { return m_leaderName.c_str(); } + LootMethod GetLootMethod() const { return m_lootMethod; } + const uint64& GetLooterGuid() const { return m_looterGuid; } + ItemQualities GetLootThreshold() const { return m_lootThreshold; } + + // member manipulation methods + bool IsMember(uint64 guid) const { return _getMemberCSlot(guid) != m_memberSlots.end(); } + bool IsLeader(uint64 guid) const { return (GetLeaderGUID() == guid); } + bool IsAssistant(uint64 guid) const + { + member_citerator mslot = _getMemberCSlot(guid); + if(mslot==m_memberSlots.end()) + return false; + + return mslot->assistant; + } + + bool SameSubGroup(uint64 guid1, uint64 guid2) const + { + member_citerator mslot2 = _getMemberCSlot(guid2); + if(mslot2==m_memberSlots.end()) + return false; + + return SameSubGroup(guid1,&*mslot2); + } + + bool SameSubGroup(uint64 guid1, MemberSlot const* slot2) const + { + member_citerator mslot1 = _getMemberCSlot(guid1); + if(mslot1==m_memberSlots.end() || !slot2) + return false; + + return (mslot1->group==slot2->group); + } + + bool SameSubGroup(Player const* member1, Player const* member2) const; + + MemberSlotList const& GetMemberSlots() const { return m_memberSlots; } + GroupReference* GetFirstMember() { return m_memberMgr.getFirst(); } + uint32 GetMembersCount() const { return m_memberSlots.size(); } + void GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_level, Player* & member_with_max_level); + uint8 GetMemberGroup(uint64 guid) const + { + member_citerator mslot = _getMemberCSlot(guid); + if(mslot==m_memberSlots.end()) + return (MAXRAIDSIZE/MAXGROUPSIZE+1); + + return mslot->group; + } + + // some additional raid methods + void ConvertToRaid() + { + _convertToRaid(); + SendUpdate(); + } + void SetBattlegroundGroup(BattleGround *bg) { m_bgGroup = bg; } + + void ChangeMembersGroup(const uint64 &guid, const uint8 &group); + void ChangeMembersGroup(Player *player, const uint8 &group); + + void SetAssistant(const uint64 &guid, const bool &state) + { + if(!isRaidGroup()) + return; + if(_setAssistantFlag(guid, state)) + SendUpdate(); + } + void SetMainTank(const uint64 &guid) + { + if(!isRaidGroup()) + return; + + if(_setMainTank(guid)) + SendUpdate(); + } + void SetMainAssistant(const uint64 &guid) + { + if(!isRaidGroup()) + return; + + if(_setMainAssistant(guid)) + SendUpdate(); + } + + void SetTargetIcon(uint8 id, uint64 guid); + void SetDifficulty(uint8 difficulty); + uint8 GetDifficulty() { return m_difficulty; } + uint16 InInstance(); + bool InCombatToInstance(uint32 instanceId); + void ResetInstances(uint8 method, Player* SendMsgTo); + + // -no description- + //void SendInit(WorldSession *session); + void SendTargetIconList(WorldSession *session); + void SendUpdate(); + void UpdatePlayerOutOfRange(Player* pPlayer); + // ignore: GUID of player that will be ignored + void BroadcastPacket(WorldPacket *packet, int group=-1, uint64 ignore=0); + void BroadcastReadyCheck(WorldPacket *packet); + void OfflineReadyCheck(); + + /*********************************************************/ + /*** LOOT SYSTEM ***/ + /*********************************************************/ + + void SendLootStartRoll(uint32 CountDown, const Roll &r); + void SendLootRoll(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r); + void SendLootRollWon(uint64 SourceGuid, uint64 TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r); + void SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r); + void GroupLoot(uint64 playerGUID, Loot *loot, Creature *creature); + void NeedBeforeGreed(uint64 playerGUID, Loot *loot, Creature *creature); + void MasterLoot(uint64 playerGUID, Loot *loot, Creature *creature); + Rolls::iterator GetRoll(uint64 Guid) + { + Rolls::iterator iter; + for (iter=RollId.begin(); iter != RollId.end(); ++iter) + { + if ((*iter)->itemGUID == Guid && (*iter)->isValid()) + { + return iter; + } + } + return RollId.end(); + } + void CountTheRoll(Rolls::iterator roll, uint32 NumberOfPlayers); + void CountRollVote(uint64 playerGUID, uint64 Guid, uint32 NumberOfPlayers, uint8 Choise); + void EndRoll(); + + void LinkMember(GroupReference *pRef) { m_memberMgr.insertFirst(pRef); } + void DelinkMember(GroupReference* /*pRef*/ ) { } + + InstanceGroupBind* BindToInstance(InstanceSave *save, bool permanent, bool load = false); + void UnbindInstance(uint32 mapid, uint8 difficulty, bool unload = false); + InstanceGroupBind* GetBoundInstance(uint32 mapid, uint8 difficulty); + BoundInstancesMap& GetBoundInstances(uint8 difficulty) { return m_boundInstances[difficulty]; } + + protected: + bool _addMember(const uint64 &guid, const char* name, bool isAssistant=false); + bool _addMember(const uint64 &guid, const char* name, bool isAssistant, uint8 group); + bool _removeMember(const uint64 &guid); // returns true if leader has changed + void _setLeader(const uint64 &guid); + + void _removeRolls(const uint64 &guid); + + void _convertToRaid(); + bool _setMembersGroup(const uint64 &guid, const uint8 &group); + bool _setAssistantFlag(const uint64 &guid, const bool &state); + bool _setMainTank(const uint64 &guid); + bool _setMainAssistant(const uint64 &guid); + + void _homebindIfInstance(Player *player); + + member_citerator _getMemberCSlot(uint64 Guid) const + { + for(member_citerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr) + { + if (itr->guid == Guid) + return itr; + } + return m_memberSlots.end(); + } + + member_witerator _getMemberWSlot(uint64 Guid) + { + for(member_witerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr) + { + if (itr->guid == Guid) + return itr; + } + return m_memberSlots.end(); + } + + MemberSlotList m_memberSlots; + GroupRefManager m_memberMgr; + InvitesList m_invitees; + uint64 m_leaderGuid; + std::string m_leaderName; + uint64 m_mainTank; + uint64 m_mainAssistant; + GroupType m_groupType; + uint8 m_difficulty; + BattleGround* m_bgGroup; + uint64 m_targetIcons[TARGETICONCOUNT]; + LootMethod m_lootMethod; + ItemQualities m_lootThreshold; + uint64 m_looterGuid; + Rolls RollId; + BoundInstancesMap m_boundInstances[TOTAL_DIFFICULTIES]; +}; +#endif diff --git a/src/game/GroupHandler.cpp b/src/game/GroupHandler.cpp new file mode 100644 index 00000000000..f2c05e14f5f --- /dev/null +++ b/src/game/GroupHandler.cpp @@ -0,0 +1,934 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "Opcodes.h" +#include "Log.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "Group.h" +#include "ObjectAccessor.h" +#include "MapManager.h" +#include "SocialMgr.h" +#include "Util.h" + +/* differeces from off: + -you can uninvite yourself - is is useful + -you can accept invitation even if leader went offline +*/ +/* todo: + -group_destroyed msg is sent but not shown + -reduce xp gaining when in raid group + -quest sharing has to be corrected + -FIX sending PartyMemberStats +*/ + +void WorldSession::SendPartyResult(PartyOperation operation, std::string member, PartyResult res) +{ + WorldPacket data(SMSG_PARTY_COMMAND_RESULT, (8+member.size()+1)); + data << (uint32)operation; + data << member; + data << (uint32)res; + + SendPacket( &data ); +} + +void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data ) +{ + std::string membername; + recv_data >> membername; + + if(_player->InBattleGround()) + { + SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_INVITE_RESTRICTED); + return; + } + + // attempt add selected player + + // cheating + if(!normalizePlayerName(membername)) + { + SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_CANT_FIND_TARGET); + return; + } + + Player *player = objmgr.GetPlayer(membername.c_str()); + + // no player + if(!player) + { + SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_CANT_FIND_TARGET); + return; + } + + // can't group with + if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && GetPlayer()->GetTeam() != player->GetTeam()) + { + SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_TARGET_UNFRIENDLY); + return; + } + if(GetPlayer()->GetInstanceId() != 0 && player->GetInstanceId() != 0 && GetPlayer()->GetInstanceId() != player->GetInstanceId() && GetPlayer()->GetMapId() == player->GetMapId()) + { + SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_NOT_IN_YOUR_INSTANCE); + return; + } + // just ignore us + if(player->GetInstanceId() != 0 && player->GetDifficulty() != GetPlayer()->GetDifficulty()) + { + SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_TARGET_IGNORE_YOU); + return; + } + + if(player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) + { + SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_TARGET_IGNORE_YOU); + return; + } + + // player already in another group or invited + if(player->GetGroup() || player->GetGroupInvite() ) + { + SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_ALREADY_IN_GROUP); + return; + } + + Group *group = GetPlayer()->GetGroup(); + + if(group) + { + // not have permissions for invite + if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID())) + { + SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_YOU_NOT_LEADER); + return; + } + + // not have place + if(group->IsFull()) + { + SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_PARTY_FULL); + return; + } + } + + // ok, but group not exist, start a new group + // but don't create and save the group to the DB until + // at least one person joins + if(!group) + { + group = new Group; + // new group: if can't add then delete + if(!group->AddLeaderInvite(GetPlayer())) + { + delete group; + return; + } + if(!group->AddInvite(player)) + { + delete group; + return; + } + } + else + { + // already existed group: if can't add then just leave + if(!group->AddInvite(player)) + { + return; + } + } + + // ok, we do it + WorldPacket data(SMSG_GROUP_INVITE, 10); // guess size + data << GetPlayer()->GetName(); + player->GetSession()->SendPacket(&data); + + SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_OK); +} + +void WorldSession::HandleGroupAcceptOpcode( WorldPacket & /*recv_data*/ ) +{ + Group *group = GetPlayer()->GetGroupInvite(); + if (!group) return; + + if(group->GetLeaderGUID() == GetPlayer()->GetGUID()) + { + sLog.outError("HandleGroupAcceptOpcode: player %s(%d) tried to accept an invite to his own group", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow()); + return; + } + + // remove in from ivites in any case + group->RemoveInvite(GetPlayer()); + + /** error handling **/ + /********************/ + + // not have place + if(group->IsFull()) + { + SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_PARTY_FULL); + return; + } + + Player* leader = objmgr.GetPlayer(group->GetLeaderGUID()); + + if(leader && leader->InBattleGround()) + { + SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_INVITE_RESTRICTED); + return; + } + + // forming a new group, create it + if(!group->IsCreated()) + { + if(leader) group->RemoveInvite(leader); + group->Create(group->GetLeaderGUID(), group->GetLeaderName()); + objmgr.AddGroup(group); + } + + // everything's fine, do it + if(!group->AddMember(GetPlayer()->GetGUID(), GetPlayer()->GetName())) + return; + + uint8 subgroup = group->GetMemberGroup(GetPlayer()->GetGUID()); + + GetPlayer()->SetGroup(group, subgroup); +} + +void WorldSession::HandleGroupDeclineOpcode( WorldPacket & /*recv_data*/ ) +{ + Group *group = GetPlayer()->GetGroupInvite(); + if (!group) return; + + Player *leader = objmgr.GetPlayer(group->GetLeaderGUID()); + + /** error handling **/ + if(!leader || !leader->GetSession()) + return; + /********************/ + + // everything's fine, do it + if(!group->IsCreated()) + { + // note: this means that if you invite more than one person + // and one of them declines before the first one accepts + // all invites will be cleared + // fixme: is that ok ? + group->RemoveAllInvites(); + delete group; + } + + GetPlayer()->SetGroupInvite(NULL); + + WorldPacket data( SMSG_GROUP_DECLINE, 10 ); // guess size + data << GetPlayer()->GetName(); + leader->GetSession()->SendPacket( &data ); +} + +void WorldSession::HandleGroupUninviteGuidOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid; + recv_data >> guid; + + if(_player->InBattleGround()) + { + SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_INVITE_RESTRICTED); + return; + } + + std::string membername; + if(!objmgr.GetPlayerNameByGUID(guid, membername)) + return; // not found + + HandleGroupUninvite(guid, membername); +} + +void WorldSession::HandleGroupUninviteNameOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data,1); + + std::string membername; + recv_data >> membername; + + if(_player->InBattleGround()) + { + SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_INVITE_RESTRICTED); + return; + } + + // player not found + if(!normalizePlayerName(membername)) + return; + + uint64 guid = objmgr.GetPlayerGUIDByName(membername); + + // player not found + if(!guid) + return; + + HandleGroupUninvite(guid, membername); +} + +void WorldSession::HandleGroupUninvite(uint64 guid, std::string name) +{ + Group *group = GetPlayer()->GetGroup(); + if(!group) + return; + + if(_player->InBattleGround()) + { + SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_INVITE_RESTRICTED); + return; + } + + Player *player = objmgr.GetPlayer(guid); + + /** error handling **/ + if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID())) + { + SendPartyResult(PARTY_OP_LEAVE, "", PARTY_RESULT_YOU_NOT_LEADER); + return; + } + + if(!group->IsMember(guid) && (player && player->GetGroupInvite() != group)) + { + SendPartyResult(PARTY_OP_LEAVE, name, PARTY_RESULT_NOT_IN_YOUR_PARTY); + return; + } + + if(guid == GetPlayer()->GetGUID()) + { + sLog.outError("WorldSession::HandleGroupUninvite: leader %s(%d) tried to uninvite himself from the group.", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow()); + return; + } + /********************/ + + // everything's fine, do it + + if(player && player->GetGroupInvite()) // uninvite invitee + player->UninviteFromGroup(); + else // uninvite member + Player::RemoveFromGroup(group,guid); +} + +void WorldSession::HandleGroupSetLeaderOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + Group *group = GetPlayer()->GetGroup(); + if(!group) + return; + + uint64 guid; + recv_data >> guid; + + Player *player = objmgr.GetPlayer(guid); + + /** error handling **/ + if (!player || !group->IsLeader(GetPlayer()->GetGUID()) || player->GetGroup() != group) + return; + /********************/ + + // everything's fine, do it + group->ChangeLeader(guid); +} + +void WorldSession::HandleGroupLeaveOpcode( WorldPacket & /*recv_data*/ ) +{ + if(!GetPlayer()->GetGroup()) + return; + + if(_player->InBattleGround()) + { + SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_INVITE_RESTRICTED); + return; + } + + /** error handling **/ + /********************/ + + // everything's fine, do it + SendPartyResult(PARTY_OP_LEAVE, GetPlayer()->GetName(), PARTY_RESULT_OK); + + GetPlayer()->RemoveFromGroup(); +} + +void WorldSession::HandleLootMethodOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+8+4); + + Group *group = GetPlayer()->GetGroup(); + if(!group) + return; + + uint32 lootMethod; + uint64 lootMaster; + uint32 lootThreshold; + recv_data >> lootMethod >> lootMaster >> lootThreshold; + + /** error handling **/ + if(!group->IsLeader(GetPlayer()->GetGUID())) + return; + /********************/ + + // everything's fine, do it + group->SetLootMethod((LootMethod)lootMethod); + group->SetLooterGuid(lootMaster); + group->SetLootThreshold((ItemQualities)lootThreshold); + group->SendUpdate(); +} + +void WorldSession::HandleLootRoll( WorldPacket &recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4+1); + + if(!GetPlayer()->GetGroup()) + return; + + uint64 Guid; + uint32 NumberOfPlayers; + uint8 Choise; + recv_data >> Guid; //guid of the item rolled + recv_data >> NumberOfPlayers; + recv_data >> Choise; //0: pass, 1: need, 2: greed + + //sLog.outDebug("WORLD RECIEVE CMSG_LOOT_ROLL, From:%u, Numberofplayers:%u, Choise:%u", (uint32)Guid, NumberOfPlayers, Choise); + + Group* group = GetPlayer()->GetGroup(); + if(!group) + return; + + // everything's fine, do it + group->CountRollVote(GetPlayer()->GetGUID(), Guid, NumberOfPlayers, Choise); +} + +void WorldSession::HandleMinimapPingOpcode(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data,4+4); + + if(!GetPlayer()->GetGroup()) + return; + + float x, y; + recv_data >> x; + recv_data >> y; + + //sLog.outDebug("Received opcode MSG_MINIMAP_PING X: %f, Y: %f", x, y); + + /** error handling **/ + /********************/ + + // everything's fine, do it + WorldPacket data(MSG_MINIMAP_PING, (8+4+4)); + data << GetPlayer()->GetGUID(); + data << x; + data << y; + GetPlayer()->GetGroup()->BroadcastPacket(&data, -1, GetPlayer()->GetGUID()); +} + +void WorldSession::HandleRandomRollOpcode(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data,4+4); + + uint32 minimum, maximum, roll; + recv_data >> minimum; + recv_data >> maximum; + + /** error handling **/ + if(minimum > maximum || maximum > 10000) // < 32768 for urand call + return; + /********************/ + + // everything's fine, do it + roll = urand(minimum, maximum); + + //sLog.outDebug("ROLL: MIN: %u, MAX: %u, ROLL: %u", minimum, maximum, roll); + + WorldPacket data(MSG_RANDOM_ROLL, 4+4+4+8); + data << minimum; + data << maximum; + data << roll; + data << GetPlayer()->GetGUID(); + if(GetPlayer()->GetGroup()) + GetPlayer()->GetGroup()->BroadcastPacket(&data); + else + SendPacket(&data); +} + +void WorldSession::HandleRaidIconTargetOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,1); + + Group *group = GetPlayer()->GetGroup(); + if(!group) + return; + + uint8 x; + recv_data >> x; + + /** error handling **/ + /********************/ + + // everything's fine, do it + if(x == 0xFF) // target icon request + { + group->SendTargetIconList(this); + } + else // target icon update + { + // recheck + CHECK_PACKET_SIZE(recv_data,1+8); + + if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID())) + return; + + uint64 guid; + recv_data >> guid; + group->SetTargetIcon(x, guid); + } +} + +void WorldSession::HandleRaidConvertOpcode( WorldPacket & /*recv_data*/ ) +{ + Group *group = GetPlayer()->GetGroup(); + if(!group) + return; + + if(_player->InBattleGround()) + return; + + /** error handling **/ + if(!group->IsLeader(GetPlayer()->GetGUID()) || group->GetMembersCount() < 2) + return; + /********************/ + + // everything's fine, do it (is it 0 (PARTY_OP_INVITE) correct code) + SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_OK); + group->ConvertToRaid(); +} + +void WorldSession::HandleGroupChangeSubGroupOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,1+1); + + Group *group = GetPlayer()->GetGroup(); + if(!group) + return; + + std::string name; + uint8 groupNr; + recv_data >> name; + + // recheck + CHECK_PACKET_SIZE(recv_data,(name.size()+1)+1); + + recv_data >> groupNr; + + /** error handling **/ + if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID())) + return; + /********************/ + + // everything's fine, do it + group->ChangeMembersGroup(objmgr.GetPlayer(name.c_str()), groupNr); +} + +void WorldSession::HandleGroupAssistantOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+1); + + Group *group = GetPlayer()->GetGroup(); + if(!group) + return; + + uint64 guid; + uint8 flag; + recv_data >> guid; + recv_data >> flag; + + /** error handling **/ + if(!group->IsLeader(GetPlayer()->GetGUID())) + return; + /********************/ + + // everything's fine, do it + group->SetAssistant(guid, (flag==0?false:true)); +} + +void WorldSession::HandleGroupPromoteOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 1+1+8); + + Group *group = GetPlayer()->GetGroup(); + if(!group) + return; + + uint8 flag1, flag2; + uint64 guid; + recv_data >> flag1 >> flag2; + recv_data >> guid; + // if(flag1) Main Assist + // 0x4 + // if(flag2) Main Tank + // 0x2 + + /** error handling **/ + if(!group->IsLeader(GetPlayer()->GetGUID())) + return; + /********************/ + + // everything's fine, do it + if(flag1 == 1) + group->SetMainAssistant(guid); + if(flag2 == 1) + group->SetMainTank(guid); +} + +void WorldSession::HandleRaidReadyCheckOpcode( WorldPacket & recv_data ) +{ + Group *group = GetPlayer()->GetGroup(); + if(!group) + return; + + if(recv_data.empty()) // request + { + /** error handling **/ + if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID())) + return; + /********************/ + + // everything's fine, do it + WorldPacket data(MSG_RAID_READY_CHECK, 8); + data << GetPlayer()->GetGUID(); + group->BroadcastPacket(&data, -1); + + group->OfflineReadyCheck(); + } + else // answer + { + uint8 state; + recv_data >> state; + + // everything's fine, do it + WorldPacket data(MSG_RAID_READY_CHECK_CONFIRM, 9); + data << GetPlayer()->GetGUID(); + data << state; + group->BroadcastReadyCheck(&data); + } +} + +void WorldSession::HandleRaidReadyCheckFinishOpcode( WorldPacket & recv_data ) +{ + //Group* group = GetPlayer()->GetGroup(); + //if(!group) + // return; + + //if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID())) + // return; + + // Is any reaction need? +} + +void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacket *data) +{ + uint32 mask = player->GetGroupUpdateFlag(); + + if (mask == GROUP_UPDATE_FLAG_NONE) + return; + + if (mask & GROUP_UPDATE_FLAG_POWER_TYPE) // if update power type, update current/max power also + mask |= (GROUP_UPDATE_FLAG_CUR_POWER | GROUP_UPDATE_FLAG_MAX_POWER); + + if (mask & GROUP_UPDATE_FLAG_PET_POWER_TYPE) // same for pets + mask |= (GROUP_UPDATE_FLAG_PET_CUR_POWER | GROUP_UPDATE_FLAG_PET_MAX_POWER); + + uint32 byteCount = 0; + for (int i = 1; i < GROUP_UPDATE_FLAGS_COUNT; ++i) + if (mask & (1 << i)) + byteCount += GroupUpdateLength[i]; + + data->Initialize(SMSG_PARTY_MEMBER_STATS, 8 + 4 + byteCount); + data->append(player->GetPackGUID()); + *data << (uint32) mask; + + if (mask & GROUP_UPDATE_FLAG_STATUS) + { + if (player) + { + if (player->IsPvP()) + *data << (uint16) (MEMBER_STATUS_ONLINE | MEMBER_STATUS_PVP); + else + *data << (uint16) MEMBER_STATUS_ONLINE; + } + else + *data << (uint16) MEMBER_STATUS_OFFLINE; + } + + if (mask & GROUP_UPDATE_FLAG_CUR_HP) + *data << (uint16) player->GetHealth(); + + if (mask & GROUP_UPDATE_FLAG_MAX_HP) + *data << (uint16) player->GetMaxHealth(); + + Powers powerType = player->getPowerType(); + if (mask & GROUP_UPDATE_FLAG_POWER_TYPE) + *data << (uint8) powerType; + + if (mask & GROUP_UPDATE_FLAG_CUR_POWER) + *data << (uint16) player->GetPower(powerType); + + if (mask & GROUP_UPDATE_FLAG_MAX_POWER) + *data << (uint16) player->GetMaxPower(powerType); + + if (mask & GROUP_UPDATE_FLAG_LEVEL) + *data << (uint16) player->getLevel(); + + if (mask & GROUP_UPDATE_FLAG_ZONE) + *data << (uint16) player->GetZoneId(); + + if (mask & GROUP_UPDATE_FLAG_POSITION) + *data << (uint16) player->GetPositionX() << (uint16) player->GetPositionY(); + + if (mask & GROUP_UPDATE_FLAG_AURAS) + { + uint64 auramask = player->GetAuraUpdateMask(); + *data << uint64(auramask); + for(uint32 i = 0; i < MAX_AURAS; ++i) + { + if(auramask & (uint64(1) << i)) + { + *data << uint16(player->GetUInt32Value(UNIT_FIELD_AURA + i)); + *data << uint8(1); + } + } + } + + Pet *pet = player->GetPet(); + if (mask & GROUP_UPDATE_FLAG_PET_GUID) + { + if(pet) + *data << (uint64) pet->GetGUID(); + else + *data << (uint64) 0; + } + + if (mask & GROUP_UPDATE_FLAG_PET_NAME) + { + if(pet) + *data << pet->GetName(); + else + *data << (uint8) 0; + } + + if (mask & GROUP_UPDATE_FLAG_PET_MODEL_ID) + { + if(pet) + *data << (uint16) pet->GetDisplayId(); + else + *data << (uint16) 0; + } + + if (mask & GROUP_UPDATE_FLAG_PET_CUR_HP) + { + if(pet) + *data << (uint16) pet->GetHealth(); + else + *data << (uint16) 0; + } + + if (mask & GROUP_UPDATE_FLAG_PET_MAX_HP) + { + if(pet) + *data << (uint16) pet->GetMaxHealth(); + else + *data << (uint16) 0; + } + + if (mask & GROUP_UPDATE_FLAG_PET_POWER_TYPE) + { + if(pet) + *data << (uint8) pet->getPowerType(); + else + *data << (uint8) 0; + } + + if (mask & GROUP_UPDATE_FLAG_PET_CUR_POWER) + { + if(pet) + *data << (uint16) pet->GetPower(pet->getPowerType()); + else + *data << (uint16) 0; + } + + if (mask & GROUP_UPDATE_FLAG_PET_MAX_POWER) + { + if(pet) + *data << (uint16) pet->GetMaxPower(pet->getPowerType()); + else + *data << (uint16) 0; + } + + if (mask & GROUP_UPDATE_FLAG_PET_AURAS) + { + if(pet) + { + uint64 auramask = pet->GetAuraUpdateMask(); + *data << uint64(auramask); + for(uint32 i = 0; i < MAX_AURAS; ++i) + { + if(auramask & (uint64(1) << i)) + { + *data << uint16(pet->GetUInt32Value(UNIT_FIELD_AURA + i)); + *data << uint8(1); + } + } + } + else + *data << (uint64) 0; + } +} + +/*this procedure handles clients CMSG_REQUEST_PARTY_MEMBER_STATS request*/ +void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + sLog.outDebug("WORLD: Received CMSG_REQUEST_PARTY_MEMBER_STATS"); + uint64 Guid; + recv_data >> Guid; + + Player *player = objmgr.GetPlayer(Guid); + if(!player) + { + WorldPacket data(SMSG_PARTY_MEMBER_STATS_FULL, 3+4+2); + data.appendPackGUID(Guid); + data << (uint32) GROUP_UPDATE_FLAG_STATUS; + data << (uint16) MEMBER_STATUS_OFFLINE; + SendPacket(&data); + return; + } + + Pet *pet = player->GetPet(); + + WorldPacket data(SMSG_PARTY_MEMBER_STATS_FULL, 4+2+2+2+1+2*6+8+1+8); + data.append(player->GetPackGUID()); + + uint32 mask1 = 0x00040BFF; // common mask, real flags used 0x000040BFF + if(pet) + mask1 = 0x7FFFFFFF; // for hunters and other classes with pets + + Powers powerType = player->getPowerType(); + data << (uint32) mask1; // group update mask + data << (uint16) MEMBER_STATUS_ONLINE; // member's online status + data << (uint16) player->GetHealth(); // GROUP_UPDATE_FLAG_CUR_HP + data << (uint16) player->GetMaxHealth(); // GROUP_UPDATE_FLAG_MAX_HP + data << (uint8) powerType; // GROUP_UPDATE_FLAG_POWER_TYPE + data << (uint16) player->GetPower(powerType); // GROUP_UPDATE_FLAG_CUR_POWER + data << (uint16) player->GetMaxPower(powerType); // GROUP_UPDATE_FLAG_MAX_POWER + data << (uint16) player->getLevel(); // GROUP_UPDATE_FLAG_LEVEL + data << (uint16) player->GetZoneId(); // GROUP_UPDATE_FLAG_ZONE + data << (uint16) player->GetPositionX(); // GROUP_UPDATE_FLAG_POSITION + data << (uint16) player->GetPositionY(); // GROUP_UPDATE_FLAG_POSITION + + uint64 auramask = 0; + size_t maskPos = data.wpos(); + data << (uint64) auramask; // placeholder + for(uint8 i = 0; i < MAX_AURAS; ++i) + { + if(uint32 aura = player->GetUInt32Value(UNIT_FIELD_AURA + i)) + { + auramask |= (uint64(1) << i); + data << uint16(aura); + data << uint8(1); + } + } + data.put(maskPos,auramask); // GROUP_UPDATE_FLAG_AURAS + + if(pet) + { + Powers petpowertype = pet->getPowerType(); + data << (uint64) pet->GetGUID(); // GROUP_UPDATE_FLAG_PET_GUID + data << pet->GetName(); // GROUP_UPDATE_FLAG_PET_NAME + data << (uint16) pet->GetDisplayId(); // GROUP_UPDATE_FLAG_PET_MODEL_ID + data << (uint16) pet->GetHealth(); // GROUP_UPDATE_FLAG_PET_CUR_HP + data << (uint16) pet->GetMaxHealth(); // GROUP_UPDATE_FLAG_PET_MAX_HP + data << (uint8) petpowertype; // GROUP_UPDATE_FLAG_PET_POWER_TYPE + data << (uint16) pet->GetPower(petpowertype); // GROUP_UPDATE_FLAG_PET_CUR_POWER + data << (uint16) pet->GetMaxPower(petpowertype); // GROUP_UPDATE_FLAG_PET_MAX_POWER + + uint64 petauramask = 0; + size_t petMaskPos = data.wpos(); + data << (uint64) petauramask; // placeholder + for(uint8 i = 0; i < MAX_AURAS; ++i) + { + if(uint32 petaura = pet->GetUInt32Value(UNIT_FIELD_AURA + i)) + { + petauramask |= (uint64(1) << i); + data << (uint16) petaura; + data << (uint8) 1; + } + } + data.put(petMaskPos,petauramask); // GROUP_UPDATE_FLAG_PET_AURAS + } + else + { + data << (uint8) 0; // GROUP_UPDATE_FLAG_PET_NAME + data << (uint64) 0; // GROUP_UPDATE_FLAG_PET_AURAS + } + + SendPacket(&data); +} + +/*!*/void WorldSession::HandleRequestRaidInfoOpcode( WorldPacket & /*recv_data*/ ) +{ + // every time the player checks the character screen + _player->SendRaidInfo(); +} + +/*void WorldSession::HandleGroupCancelOpcode( WorldPacket & recv_data ) +{ + sLog.outDebug( "WORLD: got CMSG_GROUP_CANCEL." ); +}*/ + +void WorldSession::HandleGroupPassOnLootOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 4); + + sLog.outDebug("WORLD: Received CMSG_GROUP_PASS_ON_LOOT"); + + uint32 unkn; + recv_data >> unkn; + + // ignore if player not loaded + if(!GetPlayer()) // needed because STATUS_AUTHED + { + if(unkn!=0) + sLog.outError("CMSG_GROUP_PASS_ON_LOOT value<>0 for not-loaded character!"); + return; + } + + if(unkn!=0) + sLog.outError("CMSG_GROUP_PASS_ON_LOOT: activation not implemented!"); +} diff --git a/src/game/GroupRefManager.h b/src/game/GroupRefManager.h new file mode 100644 index 00000000000..da849214416 --- /dev/null +++ b/src/game/GroupRefManager.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GROUPREFMANAGER +#define _GROUPREFMANAGER + +#include "Utilities/LinkedReference/RefManager.h" + +class Group; +class Player; +class GroupReference; + +class GroupRefManager : public RefManager +{ + public: + GroupReference* getFirst() { return ((GroupReference* ) RefManager::getFirst()); } +}; +#endif diff --git a/src/game/GroupReference.cpp b/src/game/GroupReference.cpp new file mode 100644 index 00000000000..33aff764e92 --- /dev/null +++ b/src/game/GroupReference.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Player.h" +#include "Group.h" +#include "GroupReference.h" + +void GroupReference::targetObjectBuildLink() +{ + // called from link() + getTarget()->LinkMember(this); +} + +void GroupReference::targetObjectDestroyLink() +{ + // called from unlink() + getTarget()->DelinkMember(this); +} + +void GroupReference::sourceObjectDestroyLink() +{ + // called from invalidate() + getTarget()->DelinkMember(this); +} diff --git a/src/game/GroupReference.h b/src/game/GroupReference.h new file mode 100644 index 00000000000..1e4453b020a --- /dev/null +++ b/src/game/GroupReference.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _GROUPREFERENCE_H +#define _GROUPREFERENCE_H + +#include "Utilities/LinkedReference/Reference.h" + +class Group; +class Player; + +class MANGOS_DLL_SPEC GroupReference : public Reference +{ + protected: + uint8 iSubGroup; + void targetObjectBuildLink(); + void targetObjectDestroyLink(); + void sourceObjectDestroyLink(); + public: + GroupReference() : Reference(), iSubGroup(0) {} + ~GroupReference() { unlink(); } + GroupReference *next() { return (GroupReference*)Reference::next(); } + uint8 getSubGroup() const { return iSubGroup; } + void setSubGroup(uint8 pSubGroup) { iSubGroup = pSubGroup; } +}; +#endif diff --git a/src/game/GuardAI.cpp b/src/game/GuardAI.cpp new file mode 100644 index 00000000000..fffbb7d4000 --- /dev/null +++ b/src/game/GuardAI.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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) +{ + 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) && i_creature.GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE) + { + //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/GuardAI.h b/src/game/GuardAI.h new file mode 100644 index 00000000000..f11dad6870e --- /dev/null +++ b/src/game/GuardAI.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_GUARDAI_H +#define MANGOS_GUARDAI_H + +#include "CreatureAI.h" +#include "Timer.h" + +class Creature; + +class MANGOS_DLL_DECL GuardAI : public CreatureAI +{ + enum GuardState + { + STATE_NORMAL = 1, + STATE_LOOK_AT_VICTIM = 2 + }; + + public: + + GuardAI(Creature &c); + + void MoveInLineOfSight(Unit *); + void AttackStart(Unit *); + void EnterEvadeMode(); + void JustDied(Unit *); + bool IsVisible(Unit *) const; + + void UpdateAI(const uint32); + static int Permissible(const Creature *); + + private: + Creature &i_creature; + uint64 i_victimGuid; + GuardState i_state; + TimeTracker i_tracker; +}; +#endif diff --git a/src/game/Guild.cpp b/src/game/Guild.cpp new file mode 100644 index 00000000000..bce616be394 --- /dev/null +++ b/src/game/Guild.cpp @@ -0,0 +1,1940 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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=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='"<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 ""; + + 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; iSendPacket(&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(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(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; + + dest.push_back(GuildItemPosCount(slot,need_space)); + 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; + + dest.push_back(GuildItemPosCount(j,need_space)); + count -= need_space; + + if(count==0) + return EQUIP_ERR_OK; + } + } + else + { + uint32 need_space = pSrcItem->GetMaxStackCount(); + if(need_space > count) + need_space = count; + + dest.push_back(GuildItemPosCount(j,need_space)); + 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); +} diff --git a/src/game/Guild.h b/src/game/Guild.h new file mode 100644 index 00000000000..3f16d80987d --- /dev/null +++ b/src/game/Guild.h @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef 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) {} + + uint8 slot; + uint8 count; +}; +typedef std::vector 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 MemberList; + typedef std::vector 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 TabListMap; + TabListMap m_TabListMap; + + /** These are actually ordered lists. The first element is the oldest entry.*/ + typedef std::list GuildEventlog; + typedef std::list 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/GuildHandler.cpp b/src/game/GuildHandler.cpp new file mode 100644 index 00000000000..a82a45fde40 --- /dev/null +++ b/src/game/GuildHandler.cpp @@ -0,0 +1,1815 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "ObjectMgr.h" +#include "Log.h" +#include "Opcodes.h" +#include "Guild.h" +#include "MapManager.h" +#include "GossipDef.h" +#include "SocialMgr.h" + +void WorldSession::HandleGuildQueryOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 4); + + uint32 guildId; + Guild *guild; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_QUERY"); + + recvPacket >> guildId; + + guild = objmgr.GetGuildById(guildId); + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + + guild->Query(this); +} + +void WorldSession::HandleGuildCreateOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string gname; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_CREATE"); + + recvPacket >> gname; + + if(GetPlayer()->GetGuildId()) + return; + + Guild *guild = new Guild; + if(!guild->create(GetPlayer()->GetGUID(),gname)) + { + delete guild; + return; + } + + objmgr.AddGuild(guild); +} + +void WorldSession::HandleGuildInviteOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string Invitedname, plname; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_INVITE"); + + Player * player = NULL; + + recvPacket >> Invitedname; + + if(normalizePlayerName(Invitedname)) + player = ObjectAccessor::Instance().FindPlayerByName(Invitedname.c_str()); + + if(!player) + { + SendGuildCommandResult(GUILD_INVITE_S, Invitedname, GUILD_PLAYER_NOT_FOUND); + return; + } + + Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + + // OK result but not send invite + if(player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) + return; + + // not let enemies sign guild charter + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != GetPlayer()->GetTeam()) + { + SendGuildCommandResult(GUILD_INVITE_S, Invitedname, GUILD_NOT_ALLIED); + return; + } + + if(player->GetGuildId()) + { + plname = player->GetName(); + SendGuildCommandResult(GUILD_INVITE_S, plname, ALREADY_IN_GUILD); + return; + } + + if(player->GetGuildIdInvited()) + { + plname = player->GetName(); + SendGuildCommandResult(GUILD_INVITE_S, plname, ALREADY_INVITED_TO_GUILD); + return; + } + + if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_INVITE)) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + sLog.outDebug("Player %s Invited %s to Join his Guild", GetPlayer()->GetName(), Invitedname.c_str()); + + player->SetGuildIdInvited(GetPlayer()->GetGuildId()); + // Put record into guildlog + guild->LogGuildEvent(GUILD_EVENT_LOG_INVITE_PLAYER, GetPlayer()->GetGUIDLow(), player->GetGUIDLow(), 0); + + WorldPacket data(SMSG_GUILD_INVITE, (8+10)); // guess size + data << GetPlayer()->GetName(); + data << guild->GetName(); + player->GetSession()->SendPacket(&data); + + //sLog.outDebug("WORLD: Sent (SMSG_GUILD_INVITE)"); +} + +void WorldSession::HandleGuildRemoveOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string plName; + uint64 plGuid; + uint32 plGuildId; + Guild *guild; + Player *player; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_REMOVE"); + + recvPacket >> plName; + + if(!normalizePlayerName(plName)) + return; + + player = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()); + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + + if(player) + { + plGuid = player->GetGUID(); + plGuildId = player->GetGuildId(); + } + else + { + plGuid = objmgr.GetPlayerGUIDByName(plName); + plGuildId = Player::GetGuildIdFromDB(plGuid); + } + + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + + if(!plGuid) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_FOUND); + return; + } + + if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_REMOVE)) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + if(plGuid == guild->GetLeader()) + { + SendGuildCommandResult(GUILD_QUIT_S, "", GUILD_LEADER_LEAVE); + return; + } + + if(GetPlayer()->GetGuildId() != plGuildId) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S); + return; + } + + guild->DelMember(plGuid); + // Put record into guildlog + guild->LogGuildEvent(GUILD_EVENT_LOG_UNINVITE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), 0); + + WorldPacket data(SMSG_GUILD_EVENT, (2+20)); // guess size + data << (uint8)GE_REMOVED; + data << (uint8)2; // strings count + data << plName; + data << GetPlayer()->GetName(); + guild->BroadcastPacket(&data); +} + +void WorldSession::HandleGuildAcceptOpcode(WorldPacket& /*recvPacket*/) +{ + Guild *guild; + Player *player = GetPlayer(); + + //sLog.outDebug("WORLD: Received CMSG_GUILD_ACCEPT"); + + guild = objmgr.GetGuildById(player->GetGuildIdInvited()); + if(!guild || player->GetGuildId()) + return; + + // not let enemies sign guild charter + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && player->GetTeam() != objmgr.GetPlayerTeamByGUID(guild->GetLeader())) + return; + + if(!guild->AddMember(GetPlayer()->GetGUID(),guild->GetLowestRank())) + return; + // Put record into guildlog + guild->LogGuildEvent(GUILD_EVENT_LOG_JOIN_GUILD, GetPlayer()->GetGUIDLow(), 0, 0); + + WorldPacket data(SMSG_GUILD_EVENT, (2+10)); // guess size + data << (uint8)GE_JOINED; + data << (uint8)1; + data << player->GetName(); + guild->BroadcastPacket(&data); + + //sLog.outDebug("WORLD: Sent (SMSG_GUILD_EVENT)"); +} + +void WorldSession::HandleGuildDeclineOpcode(WorldPacket& /*recvPacket*/) +{ + //sLog.outDebug("WORLD: Received CMSG_GUILD_DECLINE"); + + GetPlayer()->SetGuildIdInvited(0); + GetPlayer()->SetInGuild(0); +} + +void WorldSession::HandleGuildInfoOpcode(WorldPacket& /*recvPacket*/) +{ + Guild *guild; + //sLog.outDebug("WORLD: Received CMSG_GUILD_INFO"); + + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + + WorldPacket data(SMSG_GUILD_INFO, (5*4 + guild->GetName().size() + 1)); + data << guild->GetName(); + data << guild->GetCreatedDay(); + data << guild->GetCreatedMonth(); + data << guild->GetCreatedYear(); + data << guild->GetMemberSize(); + data << guild->GetMemberSize(); + + SendPacket(&data); +} + +void WorldSession::HandleGuildRosterOpcode(WorldPacket& /*recvPacket*/) +{ + //sLog.outDebug("WORLD: Received CMSG_GUILD_ROSTER"); + + Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(!guild) + return; + + guild->Roster(this); +} + +void WorldSession::HandleGuildPromoteOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string plName; + uint64 plGuid; + uint32 plGuildId; + uint32 plRankId; + Player *player; + Guild *guild; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_PROMOTE"); + + recvPacket >> plName; + + if(!normalizePlayerName(plName)) + return; + + player = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()); + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(player) + { + plGuid = player->GetGUID(); + plGuildId = player->GetGuildId(); + plRankId = player->GetRank(); + } + else + { + plGuid = objmgr.GetPlayerGUIDByName(plName); + plGuildId = Player::GetGuildIdFromDB(plGuid); + plRankId = Player::GetRankFromDB(plGuid); + } + + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + else if(!plGuid) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_FOUND); + return; + } + else if(plGuid == GetPlayer()->GetGUID()) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_NAME_INVALID); + return; + } + else if(GetPlayer()->GetGuildId() != plGuildId) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S); + return; + } + else if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_PROMOTE)) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + else if((plRankId-1) == 0 || (plRankId-1) < this->GetPlayer()->GetRank()) + return; + + if(plRankId < 1) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_INTERNAL); + return; + } + + uint32 newRankId = plRankId < guild->GetNrRanks() ? plRankId-1 : guild->GetNrRanks()-1; + + guild->ChangeRank(plGuid, newRankId); + // Put record into guildlog + guild->LogGuildEvent(GUILD_EVENT_LOG_PROMOTE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), newRankId); + + WorldPacket data(SMSG_GUILD_EVENT, (2+30)); // guess size + data << (uint8)GE_PROMOTION; + data << (uint8)3; + data << GetPlayer()->GetName(); + data << plName; + data << guild->GetRankName(newRankId); + guild->BroadcastPacket(&data); +} + +void WorldSession::HandleGuildDemoteOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string plName; + uint64 plGuid; + uint32 plGuildId; + uint32 plRankId; + Player *player; + Guild *guild; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_DEMOTE"); + + recvPacket >> plName; + + if(!normalizePlayerName(plName)) + return; + + player = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()); + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(player) + { + plGuid = player->GetGUID(); + plGuildId = player->GetGuildId(); + plRankId = player->GetRank(); + } + else + { + plGuid = objmgr.GetPlayerGUIDByName(plName); + plGuildId = Player::GetGuildIdFromDB(plGuid); + plRankId = Player::GetRankFromDB(plGuid); + } + + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + + if( !plGuid ) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_FOUND); + return; + } + + if(plGuid == GetPlayer()->GetGUID()) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_NAME_INVALID); + return; + } + + if(GetPlayer()->GetGuildId() != plGuildId) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S); + return; + } + + if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_DEMOTE)) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + if((plRankId+1) >= guild->GetNrRanks() || plRankId <= this->GetPlayer()->GetRank()) + return; + + guild->ChangeRank(plGuid, (plRankId+1)); + // Put record into guildlog + guild->LogGuildEvent(GUILD_EVENT_LOG_DEMOTE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), (plRankId+1)); + + WorldPacket data(SMSG_GUILD_EVENT, (2+30)); // guess size + data << (uint8)GE_DEMOTION; + data << (uint8)3; + data << GetPlayer()->GetName(); + data << plName; + data << guild->GetRankName(plRankId+1); + guild->BroadcastPacket(&data); +} + +void WorldSession::HandleGuildLeaveOpcode(WorldPacket& /*recvPacket*/) +{ + std::string plName; + Guild *guild; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_LEAVE"); + + guild = objmgr.GetGuildById(_player->GetGuildId()); + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + if(_player->GetGUID() == guild->GetLeader() && guild->GetMemberSize() > 1) + { + SendGuildCommandResult(GUILD_QUIT_S, "", GUILD_LEADER_LEAVE); + return; + } + + if(_player->GetGUID() == guild->GetLeader()) + { + guild->Disband(); + return; + } + + plName = _player->GetName(); + + guild->DelMember(_player->GetGUID()); + // Put record into guildlog + guild->LogGuildEvent(GUILD_EVENT_LOG_LEAVE_GUILD, _player->GetGUIDLow(), 0, 0); + + WorldPacket data(SMSG_GUILD_EVENT, (2+10)); // guess size + data << (uint8)GE_LEFT; + data << (uint8)1; + data << plName; + guild->BroadcastPacket(&data); + + //sLog.outDebug("WORLD: Sent (SMSG_GUILD_EVENT)"); + + SendGuildCommandResult(GUILD_QUIT_S, guild->GetName(), GUILD_PLAYER_NO_MORE_IN_GUILD); +} + +void WorldSession::HandleGuildDisbandOpcode(WorldPacket& /*recvPacket*/) +{ + std::string name; + Guild *guild; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_DISBAND"); + + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + if(GetPlayer()->GetGUID() != guild->GetLeader()) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + guild->Disband(); + + //sLog.outDebug("WORLD: Guild Sucefully Disbanded"); +} + +void WorldSession::HandleGuildLeaderOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + std::string name; + Player *newLeader; + uint64 newLeaderGUID; + uint32 newLeaderGuild; + Player *oldLeader = GetPlayer(); + Guild *guild; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_LEADER"); + + recvPacket >> name; + + if(!normalizePlayerName(name)) + return; + + newLeader = ObjectAccessor::Instance().FindPlayerByName(name.c_str()); + if(newLeader) + { + newLeaderGUID = newLeader->GetGUID(); + newLeaderGuild = newLeader->GetGuildId(); + } + else + { + newLeaderGUID = objmgr.GetPlayerGUIDByName(name); + newLeaderGuild = Player::GetGuildIdFromDB(newLeaderGUID); + } + guild = objmgr.GetGuildById(oldLeader->GetGuildId()); + + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + else if(!newLeaderGUID) + { + SendGuildCommandResult(GUILD_INVITE_S, name, GUILD_PLAYER_NOT_FOUND); + return; + } + if(oldLeader->GetGuildId() != newLeaderGuild) + { + SendGuildCommandResult(GUILD_INVITE_S, name, GUILD_PLAYER_NOT_IN_GUILD_S); + return; + } + if(oldLeader->GetGUID() != guild->GetLeader()) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + guild->SetLeader(newLeaderGUID); + guild->ChangeRank(oldLeader->GetGUID(), GR_OFFICER); + + WorldPacket data(SMSG_GUILD_EVENT, (2+20)); // guess size + data << (uint8)GE_LEADER_CHANGED; + data << (uint8)2; + data << oldLeader->GetName(); + data << name.c_str(); + guild->BroadcastPacket(&data); + + //sLog.outDebug("WORLD: Sent (SMSG_GUILD_EVENT)"); +} + +void WorldSession::HandleGuildMOTDOpcode(WorldPacket& recvPacket) +{ + Guild *guild; + std::string MOTD; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_MOTD"); + + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_SETMOTD)) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + if(!recvPacket.empty()) + recvPacket >> MOTD; + else + MOTD = ""; + + guild->SetMOTD(MOTD); + + WorldPacket data(SMSG_GUILD_EVENT, (2+MOTD.size()+1)); + data << (uint8)GE_MOTD; + data << (uint8)1; + data << MOTD; + guild->BroadcastPacket(&data); + + //sLog.outDebug("WORLD: Sent (SMSG_GUILD_EVENT)"); +} + +void WorldSession::HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + Guild *guild; + Player *player; + uint64 plGuid; + uint32 plGuildId; + std::string name,PNOTE; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_SET_PUBLIC_NOTE"); + + recvPacket >> name; + + if(!normalizePlayerName(name)) + return; + + player = ObjectAccessor::Instance().FindPlayerByName(name.c_str()); + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(player) + { + plGuid = player->GetGUID(); + plGuildId = player->GetGuildId(); + } + else + { + plGuid = objmgr.GetPlayerGUIDByName(name); + plGuildId = Player::GetGuildIdFromDB(plGuid); + } + + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + else if(!plGuid) + { + SendGuildCommandResult(GUILD_INVITE_S, name, GUILD_PLAYER_NOT_FOUND); + return; + } + else if(GetPlayer()->GetGuildId() != plGuildId) + { + SendGuildCommandResult(GUILD_INVITE_S, name, GUILD_PLAYER_NOT_IN_GUILD_S); + return; + } + if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_EPNOTE)) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + recvPacket >> PNOTE; + guild->SetPNOTE(plGuid, PNOTE); + + guild->Roster(this); +} + +void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + Guild *guild; + Player *player; + uint64 plGuid; + uint32 plGuildId; + std::string plName, OFFNOTE; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_SET_OFFICER_NOTE"); + + recvPacket >> plName; + + if(!normalizePlayerName(plName)) + return; + + player = ObjectAccessor::Instance().FindPlayerByName(plName.c_str()); + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(player) + { + plGuid = player->GetGUID(); + plGuildId = player->GetGuildId(); + } + else + { + plGuid = objmgr.GetPlayerGUIDByName(plName); + plGuildId = Player::GetGuildIdFromDB(plGuid); + } + + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + else if( !plGuid ) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_FOUND); + return; + } + else if(GetPlayer()->GetGuildId() != plGuildId) + { + SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S); + return; + } + if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_EOFFNOTE)) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + recvPacket >> OFFNOTE; + guild->SetOFFNOTE(plGuid, OFFNOTE); + + guild->Roster(this); +} + +void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 4+4+1+4*13); + //recvPacket.hexlike(); + + Guild *guild; + std::string rankname; + uint32 rankId; + uint32 rights, MoneyPerDay; + uint32 BankRights; + uint32 BankSlotPerDay; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_RANK"); + + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + + else if(GetPlayer()->GetGUID() != guild->GetLeader()) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + recvPacket >> rankId; + recvPacket >> rights; + recvPacket >> rankname; + recvPacket >> MoneyPerDay; + + for (int i = 0; i < GUILD_BANK_MAX_TABS; ++i) + { + recvPacket >> BankRights; + recvPacket >> BankSlotPerDay; + guild->SetBankRightsAndSlots(rankId, uint8(i), uint16(BankRights & 0xFF), uint16(BankSlotPerDay), true); + } + sLog.outDebug("WORLD: Changed RankName to %s , Rights to 0x%.4X", rankname.c_str(), rights); + + guild->SetBankMoneyPerDay(rankId, MoneyPerDay); + guild->SetRankName(rankId, rankname); + + if(rankId==GR_GUILDMASTER) // prevent loss leader rights + rights |= GR_RIGHT_ALL; + + guild->SetRankRights(rankId, rights); + + guild->Query(this); + guild->Roster(this); +} + +void WorldSession::HandleGuildAddRankOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + Guild *guild; + std::string rankname; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_ADD_RANK"); + + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + + if(GetPlayer()->GetGUID() != guild->GetLeader()) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + if(guild->GetNrRanks() >= GUILD_MAX_RANKS) // client not let create more 10 than ranks + return; + + recvPacket >> rankname; + + guild->CreateRank(rankname, GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK); + + guild->Query(this); + guild->Roster(this); +} + +void WorldSession::HandleGuildDelRankOpcode(WorldPacket& /*recvPacket*/) +{ + Guild *guild; + std::string rankname; + + //sLog.outDebug("WORLD: Received CMSG_GUILD_DEL_RANK"); + + guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + + else if(GetPlayer()->GetGUID() != guild->GetLeader()) + { + SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS); + return; + } + + guild->DelRank(); + + guild->Query(this); + guild->Roster(this); +} + +void WorldSession::SendGuildCommandResult(uint32 typecmd,std::string str,uint32 cmdresult) +{ + WorldPacket data(SMSG_GUILD_COMMAND_RESULT, (8+str.size()+1)); + data << typecmd; + data << str; + data << cmdresult; + SendPacket(&data); + + //sLog.outDebug("WORLD: Sent (SMSG_GUILD_COMMAND_RESULT)"); +} + +void WorldSession::HandleGuildChangeInfoOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + //sLog.outDebug("WORLD: Received CMSG_GUILD_INFO_TEXT"); + + std::string GINFO; + + recvPacket >> GINFO; + + Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(!guild) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD); + return; + } + + if(!guild->HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_MODIFY_GUILD_INFO)) + { + SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PERMISSIONS); + return; + } + + guild->SetGINFO(GINFO); +} + +void WorldSession::HandleGuildSaveEmblemOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 8+4+4+4+4+4); + + //sLog.outDebug("WORLD: Received MSG_SAVE_GUILD_EMBLEM"); + + uint64 vendorGuid; + + uint32 EmblemStyle; + uint32 EmblemColor; + uint32 BorderStyle; + uint32 BorderColor; + uint32 BackgroundColor; + + recvPacket >> vendorGuid; + + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorGuid,UNIT_NPC_FLAG_TABARDDESIGNER); + if (!pCreature) + { + //"That's not an emblem vendor!" + SendSaveGuildEmblem(ERR_GUILDEMBLEM_INVALIDVENDOR); + sLog.outDebug("WORLD: HandleGuildSaveEmblemOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(vendorGuid)); + return; + } + + // remove fake death + if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + + recvPacket >> EmblemStyle; + recvPacket >> EmblemColor; + recvPacket >> BorderStyle; + recvPacket >> BorderColor; + recvPacket >> BackgroundColor; + + Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()); + if(!guild) + { + //"You are not part of a guild!"; + SendSaveGuildEmblem(ERR_GUILDEMBLEM_NOGUILD); + return; + } + + if (guild->GetLeader() != GetPlayer()->GetGUID()) + { + //"Only guild leaders can create emblems." + SendSaveGuildEmblem(ERR_GUILDEMBLEM_NOTGUILDMASTER); + return; + } + + if(GetPlayer()->GetMoney() < 10*GOLD) + { + //"You can't afford to do that." + SendSaveGuildEmblem(ERR_GUILDEMBLEM_NOTENOUGHMONEY); + return; + } + + GetPlayer()->ModifyMoney(-10*GOLD); + guild->SetEmblem(EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor); + + //"Guild Emblem saved." + SendSaveGuildEmblem(ERR_GUILDEMBLEM_SUCCESS); + + guild->Query(this); +} + +void WorldSession::HandleGuildEventLogOpcode(WorldPacket& /* recvPacket */) +{ + // empty + sLog.outDebug("WORLD: Received (MSG_GUILD_EVENT_LOG_QUERY)"); + //recvPacket.hexlike(); + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId == 0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + pGuild->DisplayGuildEventlog(this); +} + +/****** GUILD BANK *******/ + +void WorldSession::HandleGuildBankGetMoneyAmount( WorldPacket & /* recv_data */ ) +{ + sLog.outDebug("WORLD: Received (MSG_GUILD_BANK_MONEY_WITHDRAWN)"); + //recv_data.hexlike(); + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId == 0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + pGuild->SendMoneyInfo(this, GetPlayer()->GetGUIDLow()); +} + +void WorldSession::HandleGuildBankGetRights( WorldPacket& /* recv_data */ ) +{ + sLog.outDebug("WORLD: Received (MSG_GUILD_PERMISSIONS)"); + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId == 0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + uint32 rankId = GetPlayer()->GetRank(); + + WorldPacket data(MSG_GUILD_PERMISSIONS, 4*15+1); + data << uint32(rankId); // guild rank id + data << uint32(pGuild->GetRankRights(rankId)); // rank rights + // money per day left + data << uint32(pGuild->GetMemberMoneyWithdrawRem(GetPlayer()->GetGUIDLow())); + data << uint8(pGuild->GetPurchasedTabs()); // tabs count + for(int i = 0; i < GUILD_BANK_MAX_TABS; ++i) + { + data << uint32(pGuild->GetBankRights(rankId, uint8(i))); + data << uint32(pGuild->GetMemberSlotWithdrawRem(GetPlayer()->GetGUIDLow(), uint8(i))); + } + SendPacket(&data); + sLog.outDebug("WORLD: Sent (MSG_GUILD_PERMISSIONS)"); +} + +/* Called when clicking on Guild bank gameobject */ +void WorldSession::HandleGuildBankQuery( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: Received (CMSG_GUILD_BANKER_ACTIVATE)"); + CHECK_PACKET_SIZE(recv_data,8+1); + uint64 GoGuid; + uint8 unk; + recv_data >> GoGuid >> unk; + + if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + return; + + if (uint32 GuildId = GetPlayer()->GetGuildId()) + { + if(Guild *pGuild = objmgr.GetGuildById(GuildId)) + { + pGuild->DisplayGuildBankTabsInfo(this); + return; + } + } + + SendGuildCommandResult(GUILD_BANK_S, "", GUILD_PLAYER_NOT_IN_GUILD); +} + +/* Called when opening guild bank tab only (first one) */ +void WorldSession::HandleGuildBankTabColon( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_QUERY_TAB)"); + CHECK_PACKET_SIZE(recv_data,8+1+1); + uint64 GoGuid; + uint8 TabId,unk1; + recv_data >> GoGuid >> TabId >> unk1; + + if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + return; + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId == 0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + // Let's update the amount of gold the player can withdraw before displaying the content + // This is usefull if money withdraw right has changed + pGuild->SendMoneyInfo(this, GetPlayer()->GetGUIDLow()); + + pGuild->DisplayGuildBankContent(this, TabId); +} + +void WorldSession::HandleGuildBankDeposit( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_DEPOSIT_MONEY)"); + CHECK_PACKET_SIZE(recv_data,8+4); + uint64 GoGuid; + uint32 money; + recv_data >> GoGuid >> money; + + if (!money) + return; + + if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + return; + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId == 0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + if (GetPlayer()->GetMoney() < money) + return; + + CharacterDatabase.BeginTransaction(); + + pGuild->SetBankMoney(pGuild->GetGuildBankMoney()+money); + GetPlayer()->ModifyMoney(-int(money)); + GetPlayer()->SaveGoldToDB(); + + CharacterDatabase.CommitTransaction(); + + // logging money + if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand("GM %s (Account: %u) deposit money (Amount: %u) to guild bank (Guild ID %u)", + _player->GetName(),_player->GetSession()->GetAccountId(),money,GuildId); + } + + // log + pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_MONEY, uint8(0), GetPlayer()->GetGUIDLow(), money); + + pGuild->DisplayGuildBankTabsInfo(this); + pGuild->DisplayGuildBankContent(this, 0); + pGuild->DisplayGuildBankMoneyUpdate(); +} + +void WorldSession::HandleGuildBankWithdraw( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_WITHDRAW_MONEY)"); + CHECK_PACKET_SIZE(recv_data,8+4); + uint64 GoGuid; + uint32 money; + recv_data >> GoGuid >> money; + + if (!money) + return; + + if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + return; + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId == 0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + if (pGuild->GetGuildBankMoney()HasRankRight(GetPlayer()->GetRank(), GR_RIGHT_WITHDRAW_GOLD)) + return; + + CharacterDatabase.BeginTransaction(); + + if (!pGuild->MemberMoneyWithdraw(money, GetPlayer()->GetGUIDLow())) + { + CharacterDatabase.RollbackTransaction(); + return; + } + + GetPlayer()->ModifyMoney(money); + GetPlayer()->SaveGoldToDB(); + + CharacterDatabase.CommitTransaction(); + + // Log + pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_MONEY, uint8(0), GetPlayer()->GetGUIDLow(), money); + + pGuild->SendMoneyInfo(this, GetPlayer()->GetGUIDLow()); + pGuild->DisplayGuildBankTabsInfo(this); + pGuild->DisplayGuildBankContent(this, 0); + pGuild->DisplayGuildBankMoneyUpdate(); +} + +void WorldSession::HandleGuildBankDepositItem( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_SWAP_ITEMS)"); + //recv_data.hexlike(); + + uint64 GoGuid; + uint8 BankToBank; + + uint8 BankTab, BankTabSlot, AutoStore, AutoStoreCount, PlayerSlot, PlayerBag, SplitedAmount = 0; + uint8 BankTabDst, BankTabSlotDst, unk2, ToChar = 1; + uint32 ItemEntry, unk1; + bool BankToChar = false; + + CHECK_PACKET_SIZE(recv_data,8+1); + recv_data >> GoGuid >> BankToBank; + if (BankToBank) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1+1+4+1+1+4+1+1); + recv_data >> BankTabDst; + recv_data >> BankTabSlotDst; + recv_data >> unk1; // always 0 + recv_data >> BankTab; + recv_data >> BankTabSlot; + recv_data >> ItemEntry; + recv_data >> unk2; // always 0 + recv_data >> SplitedAmount; + + if (BankTabSlotDst >= GUILD_BANK_MAX_SLOTS) + return; + if (BankTabDst == BankTab && BankTabSlotDst == BankTabSlot) + return; + } + else + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1+1+4+1); + recv_data >> BankTab; + recv_data >> BankTabSlot; + recv_data >> ItemEntry; + recv_data >> AutoStore; + if (AutoStore) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1); + recv_data >> AutoStoreCount; + } + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1+1); + recv_data >> PlayerBag; + recv_data >> PlayerSlot; + if (!AutoStore) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+1+1); + recv_data >> ToChar; + recv_data >> SplitedAmount; + } + + if (BankTabSlot >= GUILD_BANK_MAX_SLOTS && BankTabSlot != 0xFF) + return; + } + + if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + return; + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId == 0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + Player *pl = GetPlayer(); + + // Bank <-> Bank + if (BankToBank) + { + // empty operation + if(BankTab==BankTabDst && BankTabSlot==BankTabSlotDst) + return; + + Item *pItemSrc = pGuild->GetItem(BankTab, BankTabSlot); + if (!pItemSrc) // may prevent crash + return; + + if(SplitedAmount > pItemSrc->GetCount()) + return; // cheating? + else if(SplitedAmount == pItemSrc->GetCount()) + SplitedAmount = 0; // no split + + Item *pItemDst = pGuild->GetItem(BankTabDst, BankTabSlotDst); + + if(BankTab!=BankTabDst) + { + // check dest pos rights (if different tabs) + if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTabDst, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) + return; + + // check source pos rights (if different tabs) + uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); + if(remRight <= 0) + return; + } + + if (SplitedAmount) + { // Bank -> Bank item split (in empty or non empty slot + GuildItemPosCountVec dest; + uint8 msg = pGuild->CanStoreItem(BankTabDst,BankTabSlotDst,dest,SplitedAmount,pItemSrc,false); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemSrc, NULL ); + return; + } + + Item *pNewItem = pItemSrc->CloneItem( SplitedAmount ); + if( !pNewItem ) + { + pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemSrc, NULL ); + return; + } + + CharacterDatabase.BeginTransaction(); + pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), SplitedAmount, BankTabDst); + + pl->ItemRemovedQuestCheck( pItemSrc->GetEntry(), SplitedAmount ); + pItemSrc->SetCount( pItemSrc->GetCount() - SplitedAmount ); + pItemSrc->FSetState(ITEM_CHANGED); + pItemSrc->SaveToDB(); // not in inventory and can be save standalone + pGuild->StoreItem(BankTabDst,dest,pNewItem); + CharacterDatabase.CommitTransaction(); + } + else // non split + { + GuildItemPosCountVec gDest; + uint8 msg = pGuild->CanStoreItem(BankTabDst,BankTabSlotDst,gDest,pItemSrc->GetCount(),pItemSrc,false); + if( msg == EQUIP_ERR_OK ) // merge to + { + CharacterDatabase.BeginTransaction(); + pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), pItemSrc->GetCount(), BankTabDst); + + pGuild->RemoveItem(BankTab, BankTabSlot); + pGuild->StoreItem(BankTabDst, gDest, pItemSrc); + CharacterDatabase.CommitTransaction(); + } + else // swap + { + gDest.clear(); + uint8 msg = pGuild->CanStoreItem(BankTabDst,BankTabSlotDst,gDest,pItemSrc->GetCount(),pItemSrc,true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemSrc, NULL ); + return; + } + + GuildItemPosCountVec gSrc; + msg = pGuild->CanStoreItem(BankTab,BankTabSlot,gSrc,pItemDst->GetCount(),pItemDst,true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemDst, NULL ); + return; + } + + if(BankTab!=BankTabDst) + { + // check source pos rights (item swapped to src) + if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) + return; + + // check dest pos rights (item swapped to src) + uint32 remRightDst = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTabDst); + if(remRightDst <= 0) + return; + } + + CharacterDatabase.BeginTransaction(); + pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), pItemSrc->GetCount(), BankTabDst); + pGuild->LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTabDst, pl->GetGUIDLow(), pItemDst->GetEntry(), pItemDst->GetCount(), BankTab); + + pGuild->RemoveItem(BankTab, BankTabSlot); + pGuild->RemoveItem(BankTabDst, BankTabSlotDst); + pGuild->StoreItem(BankTab, gSrc, pItemDst); + pGuild->StoreItem(BankTabDst, gDest, pItemSrc); + CharacterDatabase.CommitTransaction(); + } + } + pGuild->DisplayGuildBankContentUpdate(BankTab,BankTabSlot,BankTab==BankTabDst ? BankTabSlotDst : -1); + if(BankTab!=BankTabDst) + pGuild->DisplayGuildBankContentUpdate(BankTabDst,BankTabSlotDst); + return; + } + + // Player <-> Bank + + // char->bank autostore click return BankTabSlot = 255 = NULL_SLOT + // do similar for bank->char + if(AutoStore && ToChar) + { + PlayerBag = NULL_BAG; + PlayerSlot = NULL_SLOT; + } + + // allow work with inventory only + if(!Player::IsInventoryPos(PlayerBag,PlayerSlot) && !(PlayerBag == NULL_BAG && PlayerSlot == NULL_SLOT) ) + { + _player->SendEquipError( EQUIP_ERR_NONE, NULL, NULL ); + return; + } + + Item *pItemBank = pGuild->GetItem(BankTab, BankTabSlot); + Item *pItemChar = GetPlayer()->GetItemByPos(PlayerBag, PlayerSlot); + if (!pItemChar && !pItemBank) // Nothing to do + return; + + if (!pItemChar && !ToChar) // Problem to get item from player + return; + + if (!pItemBank && ToChar) // Problem to get bank item + return; + + // BankToChar swap or char to bank remaining + + if (ToChar) // Bank -> Char cases + { + if(SplitedAmount > pItemBank->GetCount()) + return; // cheating? + else if(SplitedAmount == pItemBank->GetCount()) + SplitedAmount = 0; // no split + + if (SplitedAmount) + { // Bank -> Char split to slot (patly move) + Item *pNewItem = pItemBank->CloneItem( SplitedAmount ); + if( !pNewItem ) + { + pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemBank, NULL ); + return; + } + + ItemPosCountVec dest; + uint8 msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pNewItem, false); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pNewItem, NULL ); + delete pNewItem; + return; + } + + // check source pos rights (item moved to inventory) + uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); + if(remRight <= 0) + { + delete pNewItem; + return; + } + + CharacterDatabase.BeginTransaction(); + pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), SplitedAmount); + + pItemBank->SetCount(pItemBank->GetCount()-SplitedAmount); + pItemBank->FSetState(ITEM_CHANGED); + pItemBank->SaveToDB(); // not in inventory and can be save standalone + pl->MoveItemToInventory(dest,pNewItem,true); + pl->SaveInventoryAndGoldToDB(); + + pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow()); + CharacterDatabase.CommitTransaction(); + } + else // Bank -> Char swap with slot (move) + { + ItemPosCountVec dest; + uint8 msg = pl->CanStoreItem(PlayerBag, PlayerSlot, dest, pItemBank, false); + if( msg == EQUIP_ERR_OK ) // merge case + { + // check source pos rights (item moved to inventory) + uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); + if(remRight <= 0) + return; + + CharacterDatabase.BeginTransaction(); + pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount()); + + pGuild->RemoveItem(BankTab, BankTabSlot); + pl->MoveItemToInventory(dest,pItemBank,true); + pl->SaveInventoryAndGoldToDB(); + + pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow()); + CharacterDatabase.CommitTransaction(); + } + else // Bank <-> Char swap items + { + // check source pos rights (item swapped to bank) + if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) + return; + + if(pItemChar) + { + if(!pItemChar->CanBeTraded()) + { + _player->SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pItemChar, NULL ); + return; + } + } + + ItemPosCountVec iDest; + msg = pl->CanStoreItem(PlayerBag, PlayerSlot, iDest, pItemBank, true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemBank, NULL ); + return; + } + + GuildItemPosCountVec gDest; + if(pItemChar) + { + msg = pGuild->CanStoreItem(BankTab,BankTabSlot,gDest,pItemChar->GetCount(),pItemChar,true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemChar, NULL ); + return; + } + } + + // check source pos rights (item moved to inventory) + uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); + if(remRight <= 0) + return; + + if(pItemChar) + { + // logging item move to bank + if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand("GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", + _player->GetName(),_player->GetSession()->GetAccountId(), + pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(), + GuildId); + } + } + + CharacterDatabase.BeginTransaction(); + pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount()); + if(pItemChar) + pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount()); + + pGuild->RemoveItem(BankTab, BankTabSlot); + if(pItemChar) + { + pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true); + pItemChar->DeleteFromInventoryDB(); + } + + if(pItemChar) + pGuild->StoreItem(BankTab, gDest, pItemChar); + pl->MoveItemToInventory(iDest,pItemBank,true); + pl->SaveInventoryAndGoldToDB(); + + pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow()); + CharacterDatabase.CommitTransaction(); + } + } + pGuild->DisplayGuildBankContentUpdate(BankTab,BankTabSlot); + return; + } // End "To char" part + + // Char -> Bank cases + + if(!pItemChar->CanBeTraded()) + { + _player->SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pItemChar, NULL ); + return; + } + + // check source pos rights (item moved to bank) + if(!pGuild->IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM)) + return; + + if(SplitedAmount > pItemChar->GetCount()) + return; // cheating? + else if(SplitedAmount == pItemChar->GetCount()) + SplitedAmount = 0; // no split + + if (SplitedAmount) + { // Char -> Bank split to empty or non-empty slot (partly move) + GuildItemPosCountVec dest; + uint8 msg = pGuild->CanStoreItem(BankTab,BankTabSlot,dest,SplitedAmount,pItemChar,false); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemChar, NULL ); + return; + } + + Item *pNewItem = pItemChar->CloneItem( SplitedAmount ); + if( !pNewItem ) + { + pl->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, pItemChar, NULL ); + return; + } + + // logging item move to bank (before items merge + if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand("GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", + _player->GetName(),_player->GetSession()->GetAccountId(), + pItemChar->GetProto()->Name1,pItemChar->GetEntry(),SplitedAmount,GuildId); + } + + CharacterDatabase.BeginTransaction(); + pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), SplitedAmount); + + pl->ItemRemovedQuestCheck( pItemChar->GetEntry(), SplitedAmount ); + pItemChar->SetCount(pItemChar->GetCount()-SplitedAmount); + pItemChar->SetState(ITEM_CHANGED); + pl->SaveInventoryAndGoldToDB(); + pGuild->StoreItem(BankTab, dest, pNewItem); + CharacterDatabase.CommitTransaction(); + + pGuild->DisplayGuildBankContentUpdate(BankTab,dest); + } + else // Char -> Bank swap with empty or non-empty (move) + { + GuildItemPosCountVec dest; + uint8 msg = pGuild->CanStoreItem(BankTab,BankTabSlot,dest,pItemChar->GetCount(),pItemChar,false); + if( msg == EQUIP_ERR_OK ) // merge + { + // logging item move to bank + if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand("GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", + _player->GetName(),_player->GetSession()->GetAccountId(), + pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(), + GuildId); + } + + CharacterDatabase.BeginTransaction(); + pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount()); + + pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true); + pItemChar->DeleteFromInventoryDB(); + + pGuild->StoreItem(BankTab,dest,pItemChar); + pl->SaveInventoryAndGoldToDB(); + CharacterDatabase.CommitTransaction(); + + pGuild->DisplayGuildBankContentUpdate(BankTab,dest); + } + else // Char <-> Bank swap items (posible NULL bank item) + { + ItemPosCountVec iDest; + if(pItemBank) + { + msg = pl->CanStoreItem(PlayerBag, PlayerSlot, iDest, pItemBank, true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemBank, NULL ); + return; + } + } + + GuildItemPosCountVec gDest; + msg = pGuild->CanStoreItem(BankTab,BankTabSlot,gDest,pItemChar->GetCount(),pItemChar,true); + if( msg != EQUIP_ERR_OK ) + { + pl->SendEquipError( msg, pItemChar, NULL ); + return; + } + + if(pItemBank) + { + // check bank pos rights (item swapped with inventory) + uint32 remRight = pGuild->GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab); + if(remRight <= 0) + return; + } + + // logging item move to bank + if(_player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand("GM %s (Account: %u) deposit item: %s (Entry: %d Count: %u) to guild bank (Guild ID: %u )", + _player->GetName(),_player->GetSession()->GetAccountId(), + pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(), + GuildId); + } + + CharacterDatabase.BeginTransaction(); + if(pItemBank) + pGuild->LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount()); + pGuild->LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount()); + + pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true); + pItemChar->DeleteFromInventoryDB(); + if(pItemBank) + pGuild->RemoveItem(BankTab, BankTabSlot); + + pGuild->StoreItem(BankTab,gDest,pItemChar); + if(pItemBank) + pl->MoveItemToInventory(iDest,pItemBank,true); + pl->SaveInventoryAndGoldToDB(); + if(pItemBank) + pGuild->MemberItemWithdraw(BankTab, pl->GetGUIDLow()); + CharacterDatabase.CommitTransaction(); + + pGuild->DisplayGuildBankContentUpdate(BankTab,gDest); + } + } +} + +void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_BUY_TAB)"); + CHECK_PACKET_SIZE(recv_data, 8+1); + //recv_data.hexlike(); + uint64 GoGuid; + uint8 TabId; + + recv_data >> GoGuid; + recv_data >> TabId; + + if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + return; + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId==0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + uint32 TabCost = objmgr.GetGuildBankTabPrice(TabId) * GOLD; + if (!TabCost) + return; + + if (pGuild->GetPurchasedTabs() >= GUILD_BANK_MAX_TABS) + return; + + if (TabId != pGuild->GetPurchasedTabs()) // purchased_tabs = 0 when buying Tab 0, that is why this check can be made + { + sLog.outError("Error: trying to buy a tab non contigous to owned ones"); + return; + } + + if (GetPlayer()->GetMoney() < TabCost) // Should not happen, this is checked by client + return; + + // Go on with creating tab + pGuild->CreateNewBankTab(); + GetPlayer()->ModifyMoney(-int(TabCost)); + pGuild->SetBankMoneyPerDay(GetPlayer()->GetRank(), WITHDRAW_MONEY_UNLIMITED); + pGuild->SetBankRightsAndSlots(GetPlayer()->GetRank(), TabId, GUILD_BANK_RIGHT_FULL, WITHDRAW_SLOT_UNLIMITED, true); + pGuild->Roster(this); + pGuild->DisplayGuildBankTabsInfo(this); +} + +void WorldSession::HandleGuildBankModifyTab( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_UPDATE_TAB)"); + //recv_data.hexlike(); + CHECK_PACKET_SIZE(recv_data, 8+1+1+1); + uint64 GoGuid; + uint8 TabId; + std::string Name; + std::string IconIndex; + + recv_data >> GoGuid; + recv_data >> TabId; + recv_data >> Name; + recv_data >> IconIndex; + + if(Name.empty()) + return; + + if(IconIndex.empty()) + return; + + if (!objmgr.IsGuildVaultGameObject(_player, GoGuid)) + return; + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId==0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + pGuild->SetGuildBankTabInfo(TabId, Name, IconIndex); + pGuild->DisplayGuildBankTabsInfo(this); + pGuild->DisplayGuildBankContent(this, TabId); +} + +void WorldSession::HandleGuildBankLog( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: Received (MSG_GUILD_BANK_LOG_QUERY)"); + CHECK_PACKET_SIZE(recv_data, 1); + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId == 0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + uint8 TabId; + recv_data >> TabId; + + pGuild->DisplayGuildBankLogs(this, TabId); +} + +void WorldSession::HandleGuildBankTabText(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: Received MSG_QUERY_GUILD_BANK_TEXT"); + CHECK_PACKET_SIZE(recv_data, 1); + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId == 0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + uint8 TabId; + recv_data >> TabId; + + pGuild->SendGuildBankTabText(this, TabId); +} + +void WorldSession::HandleGuildBankSetTabText(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: Received CMSG_SET_GUILD_BANK_TEXT"); + CHECK_PACKET_SIZE(recv_data, 1+1); + + uint32 GuildId = GetPlayer()->GetGuildId(); + if (GuildId == 0) + return; + + Guild *pGuild = objmgr.GetGuildById(GuildId); + if(!pGuild) + return; + + uint8 TabId; + std::string Text; + recv_data >> TabId; + recv_data >> Text; + + pGuild->SetGuildBankTabText(TabId, Text); +} + +void WorldSession::SendSaveGuildEmblem( uint32 msg ) +{ + WorldPacket data(MSG_SAVE_GUILD_EMBLEM, 4); + data << uint32(msg); // not part of guild + SendPacket( &data ); +} diff --git a/src/game/HateMatrix.h b/src/game/HateMatrix.h new file mode 100644 index 00000000000..fc0cbcee625 --- /dev/null +++ b/src/game/HateMatrix.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_HATEMATRIX_H +#define MANGOS_HATEMATRIX_H + +#include "Utilities/HashMap.h" +#include + +class Unit; + +struct MANGOS_DLL_DECL HateMatrix +{ + typedef hash_map HateMatrixMapType; + + inline uint32 operator[](Unit *unit) const + { + HateMatrixMapType::const_iterator iter = i_hateValues.find(unit); + return (iter == i_hateValues.end() ? 0 : iter->second); + } + + inline uint32& operator[](Unit *unit) + { + HateMatrixMapType::iterator iter = i_hateValues.find(unit); + if( iter == i_hateValues.end() ) + { + std::pair p = i_hateValues.insert( HateMatrixMapType::value_type(unit, 0) ); + assert(p.second); + iter = p.first; + } + + return iter->second; + } + + inline void ClearMatrix(void) { i_hateValues.clear(); } + + inline void RemoveValue(Unit *unit) + { + HateMatrixMapType::iterator iter = i_hateValues.find(unit); + if( iter != i_hateValues.end() ) + i_hateValues.erase( iter ); + } + + inline void AddValue(Unit *unit, uint32 val) + { + (*this)[unit] += val; + } + + private: + HateMatrixMapType i_hateValues; +}; + +struct HateBinder +{ + static uint32 si_noHateValue; + uint32 &i_hateValue; + Unit *i_unit; + HateBinder(uint32 &val, Unit *u) : i_hateValue(val), i_unit(u) {} + HateBinder() : i_hateValue(si_noHateValue), i_unit(NULL) {} + HateBinder(const HateBinder &obj) : i_hateValue(obj.i_hateValue), i_unit(obj.i_unit) {} + + HateBinder& operator=(const HateBinder &obj) + { + i_hateValue = obj.i_hateValue; + i_unit = obj.i_unit; + return *this; + } +}; +#endif diff --git a/src/game/HomeMovementGenerator.cpp b/src/game/HomeMovementGenerator.cpp new file mode 100644 index 00000000000..281909b68de --- /dev/null +++ b/src/game/HomeMovementGenerator.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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::Initialize(Creature & owner) +{ + owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + _setTargetLocation(owner); +} + +void +HomeMovementGenerator::Reset(Creature &) +{ +} + +void +HomeMovementGenerator::_setTargetLocation(Creature & owner) +{ + if( !&owner ) + return; + + if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED) ) + return; + + float x, y, z; + owner.GetRespawnCoord(x, y, z); + + CreatureTraveller traveller(owner); + + uint32 travel_time = i_destinationHolder.SetDestination(traveller, x, y, z); + modifyTravelTime(travel_time); + owner.clearUnitState(UNIT_STAT_ALL_STATE); +} + +bool +HomeMovementGenerator::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/HomeMovementGenerator.h b/src/game/HomeMovementGenerator.h new file mode 100644 index 00000000000..aa7dc27a1b1 --- /dev/null +++ b/src/game/HomeMovementGenerator.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_HOMEMOVEMENTGENERATOR_H +#define MANGOS_HOMEMOVEMENTGENERATOR_H + +#include "MovementGenerator.h" +#include "DestinationHolder.h" +#include "Traveller.h" + +class Creature; + +template < class T > +class MANGOS_DLL_SPEC HomeMovementGenerator; + +template <> +class MANGOS_DLL_SPEC HomeMovementGenerator +: public MovementGeneratorMedium< Creature, HomeMovementGenerator > +{ + public: + + HomeMovementGenerator() {} + ~HomeMovementGenerator() {} + + void Initialize(Creature &); + void Finalize(Creature &) {} + void Reset(Creature &); + bool Update(Creature &, const uint32 &); + void modifyTravelTime(uint32 travel_time) { i_travel_timer = travel_time; } + MovementGeneratorType GetMovementGeneratorType() { return HOME_MOTION_TYPE; } + + bool GetDestination(float& x, float& y, float& z) const { i_destinationHolder.GetDestination(x,y,z); return true; } + private: + void _setTargetLocation(Creature &); + DestinationHolder< Traveller > i_destinationHolder; + + uint32 i_travel_timer; +}; +#endif diff --git a/src/game/HostilRefManager.cpp b/src/game/HostilRefManager.cpp new file mode 100644 index 00000000000..5ce8ec4d556 --- /dev/null +++ b/src/game/HostilRefManager.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "HostilRefManager.h" +#include "ThreatManager.h" +#include "Unit.h" +#include "Database/DBCStructure.h" +#include "SpellMgr.h" + +HostilRefManager::~HostilRefManager() +{ + deleteReferences(); +} + +//================================================= +// send threat to all my hateres for the pVictim +// The pVictim is hated than by them as well +// use for buffs and healing threat functionality + +void HostilRefManager::threatAssist(Unit *pVictim, float pThreat, SpellEntry const *pThreatSpell, bool pSingleTarget) +{ + HostilReference* ref; + + uint32 size = pSingleTarget ? 1 : getSize(); // if pSingleTarget do not devide threat + ref = getFirst(); + while(ref != NULL) + { + float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, (pThreatSpell ? GetSpellSchoolMask(pThreatSpell) : SPELL_SCHOOL_MASK_NORMAL), pThreatSpell); + if(pVictim == getOwner()) + ref->addThreat(float (threat) / size); // It is faster to modify the threat durectly if possible + else + ref->getSource()->addThreat(pVictim, float (threat) / size); + ref = ref->next(); + } +} + +//================================================= + +void HostilRefManager::addThreatPercent(int32 pValue) +{ + HostilReference* ref; + + ref = getFirst(); + while(ref != NULL) + { + ref->addThreatPercent(pValue); + ref = ref->next(); + } +} + +//================================================= +// The online / offline status is given to the method. The calculation has to be done before + +void HostilRefManager::setOnlineOfflineState(bool pIsOnline) +{ + HostilReference* ref; + + ref = getFirst(); + while(ref != NULL) + { + ref->setOnlineOfflineState(pIsOnline); + ref = ref->next(); + } +} + +//================================================= +// The online / offline status is calculated and set + +void HostilRefManager::updateThreatTables() +{ + HostilReference* ref = getFirst(); + while(ref) + { + ref->updateOnlineStatus(); + ref = ref->next(); + } +} + +//================================================= +// The references are not needed anymore +// tell the source to remove them from the list and free the mem + +void HostilRefManager::deleteReferences() +{ + HostilReference* ref = getFirst(); + while(ref) + { + HostilReference* nextRef = ref->next(); + ref->removeReference(); + delete ref; + ref = nextRef; + } +} + +//================================================= +// delete one reference, defined by Unit + +void HostilRefManager::deleteReference(Unit *pCreature) +{ + HostilReference* ref = getFirst(); + while(ref) + { + HostilReference* nextRef = ref->next(); + if(ref->getSource()->getOwner() == pCreature) + { + ref->removeReference(); + delete ref; + break; + } + ref = nextRef; + } +} + +//================================================= +// set state for one reference, defined by Unit + +void HostilRefManager::setOnlineOfflineState(Unit *pCreature,bool pIsOnline) +{ + HostilReference* ref = getFirst(); + while(ref) + { + HostilReference* nextRef = ref->next(); + if(ref->getSource()->getOwner() == pCreature) + { + ref->setOnlineOfflineState(pIsOnline); + break; + } + ref = nextRef; + } +} + +//================================================= diff --git a/src/game/HostilRefManager.h b/src/game/HostilRefManager.h new file mode 100644 index 00000000000..b4e4707e9fe --- /dev/null +++ b/src/game/HostilRefManager.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _HOSTILEREFMANAGER +#define _HOSTILEREFMANAGER + +#include "Common.h" +#include "Utilities/LinkedReference/RefManager.h" + +class Unit; +class ThreatManager; +class HostilReference; +struct SpellEntry; + +//================================================= + +class HostilRefManager : public RefManager +{ + private: + Unit *iOwner; + public: + explicit HostilRefManager(Unit *pOwner) { iOwner = pOwner; } + ~HostilRefManager(); + + Unit* getOwner() { return iOwner; } + + // send threat to all my hateres for the pVictim + // The pVictim is hated than by them as well + // use for buffs and healing threat functionality + void threatAssist(Unit *pVictim, float threat, SpellEntry const *threatSpell = 0, bool pSingleTarget=false); + + void addThreatPercent(int32 pValue); + + // The references are not needed anymore + // tell the source to remove them from the list and free the mem + void deleteReferences(); + + HostilReference* getFirst() { return ((HostilReference* ) RefManager::getFirst()); } + + void updateThreatTables(); + + void setOnlineOfflineState(bool pIsOnline); + + // set state for one reference, defined by Unit + void setOnlineOfflineState(Unit *pCreature,bool pIsOnline); + + // delete one reference, defined by Unit + void deleteReference(Unit *pCreature); +}; +//================================================= +#endif diff --git a/src/game/IdleMovementGenerator.cpp b/src/game/IdleMovementGenerator.cpp new file mode 100644 index 00000000000..3932bd63f1f --- /dev/null +++ b/src/game/IdleMovementGenerator.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "IdleMovementGenerator.h" +#include "Creature.h" + +IdleMovementGenerator si_idleMovement; + +void +IdleMovementGenerator::Reset(Unit& /*owner*/) +{ +} + +void +DistractMovementGenerator::Initialize(Unit& owner) +{ + owner.addUnitState(UNIT_STAT_DISTRACTED); +} + +void +DistractMovementGenerator::Finalize(Unit& owner) +{ + owner.clearUnitState(UNIT_STAT_DISTRACTED); +} + +bool +DistractMovementGenerator::Update(Unit& owner, const uint32& time_diff) +{ + if(time_diff > m_timer) + return false; + + m_timer -= time_diff; + return true; +} diff --git a/src/game/IdleMovementGenerator.h b/src/game/IdleMovementGenerator.h new file mode 100644 index 00000000000..cd1c8a20ac7 --- /dev/null +++ b/src/game/IdleMovementGenerator.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_IDLEMOVEMENTGENERATOR_H +#define MANGOS_IDLEMOVEMENTGENERATOR_H + +#include "MovementGenerator.h" + +class MANGOS_DLL_SPEC IdleMovementGenerator : public MovementGenerator +{ + public: + + void Initialize(Unit &) { } + void Finalize(Unit &) { } + void Reset(Unit &); + bool Update(Unit &, const uint32 &) { return true; } + MovementGeneratorType GetMovementGeneratorType() { return IDLE_MOTION_TYPE; } +}; + +extern IdleMovementGenerator si_idleMovement; + +class MANGOS_DLL_SPEC DistractMovementGenerator : public MovementGenerator +{ + public: + explicit DistractMovementGenerator(uint32 timer) : m_timer(timer) {} + + void Initialize(Unit& owner); + void Finalize(Unit& owner); + void Reset(Unit& owner) { Initialize(owner); } + bool Update(Unit& owner, const uint32& time_diff); + MovementGeneratorType GetMovementGeneratorType() { return DISTRACT_MOTION_TYPE; } + + private: + uint32 m_timer; +}; + +#endif diff --git a/src/game/InstanceData.cpp b/src/game/InstanceData.cpp new file mode 100644 index 00000000000..78c7bea2584 --- /dev/null +++ b/src/game/InstanceData.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "InstanceData.h" +#include "Database/DatabaseEnv.h" +#include "Map.h" + +void InstanceData::SaveToDB() +{ + if(!Save()) return; + std::string data = Save(); + CharacterDatabase.escape_string(data); + CharacterDatabase.PExecute("UPDATE instance SET data = '%s' WHERE id = '%d'", data.c_str(), instance->GetInstanceId()); +} diff --git a/src/game/InstanceData.h b/src/game/InstanceData.h new file mode 100644 index 00000000000..518fa8661fd --- /dev/null +++ b/src/game/InstanceData.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_INSTANCE_DATA_H +#define MANGOS_INSTANCE_DATA_H + +#include "Common.h" + +class Map; +class Unit; +class Player; +class GameObject; +class Creature; + +class MANGOS_DLL_SPEC InstanceData +{ + public: + + explicit InstanceData(Map *map) : instance(map) {} + virtual ~InstanceData() {} + + Map *instance; + + //On creation, NOT load. + virtual void Initialize() {} + + //On load + virtual void Load(const char* /*data*/) {} + + //When save is needed, this function generates the data + virtual const char* Save() { return ""; } + + void SaveToDB(); + + //Called every map update + virtual void Update(uint32 /*diff*/) {} + + //Used by the map's CanEnter function. + //This is to prevent players from entering during boss encounters. + virtual bool IsEncounterInProgress() const { return false; }; + + //Called when a player successfully enters the instance. + virtual void OnPlayerEnter(Player *) {} + + //Called when a gameobject is created + virtual void OnObjectCreate(GameObject *) {} + + //called on creature creation + virtual void OnCreatureCreate(Creature * /*creature*/, uint32 /*creature_entry*/) {} +}; +#endif diff --git a/src/game/InstanceSaveMgr.cpp b/src/game/InstanceSaveMgr.cpp new file mode 100644 index 00000000000..aedf7015b3a --- /dev/null +++ b/src/game/InstanceSaveMgr.cpp @@ -0,0 +1,649 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "InstanceSaveMgr.h" +#include "Common.h" +#include "Database/SQLStorage.h" + +#include "Player.h" +#include "GridNotifiers.h" +#include "WorldSession.h" +#include "Log.h" +#include "GridStates.h" +#include "CellImpl.h" +#include "Map.h" +#include "MapManager.h" +#include "MapInstanced.h" +#include "InstanceSaveMgr.h" +#include "Timer.h" +#include "GridNotifiersImpl.h" +#include "Config/ConfigEnv.h" +#include "Transports.h" +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "World.h" +#include "Group.h" +#include "InstanceData.h" +#include "ProgressBar.h" + +INSTANTIATE_SINGLETON_1( InstanceSaveManager ); + +InstanceSaveManager::InstanceSaveManager() : lock_instLists(false) +{ +} + +InstanceSaveManager::~InstanceSaveManager() +{ + // it is undefined whether this or objectmgr will be unloaded first + // so we must be prepared for both cases + lock_instLists = true; + for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr) + { + InstanceSave *save = itr->second; + for(InstanceSave::PlayerListType::iterator itr = save->m_playerList.begin(), next = itr; itr != save->m_playerList.end(); itr = next) + { + ++next; + (*itr)->UnbindInstance(save->GetMapId(), save->GetDifficulty(), true); + } + save->m_playerList.clear(); + for(InstanceSave::GroupListType::iterator itr = save->m_groupList.begin(), next = itr; itr != save->m_groupList.end(); itr = next) + { + ++next; + (*itr)->UnbindInstance(save->GetMapId(), save->GetDifficulty(), true); + } + save->m_groupList.clear(); + delete save; + } +} + +/* +- adding instance into manager +- called from InstanceMap::Add, _LoadBoundInstances, LoadGroups +*/ +InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instanceId, uint8 difficulty, time_t resetTime, bool canReset, bool load) +{ + InstanceSave *save = GetInstanceSave(instanceId); + if(save) return save; + + const MapEntry* entry = sMapStore.LookupEntry(mapId); + if(!entry || instanceId == 0) + { + sLog.outError("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d!", mapId, instanceId); + return NULL; + } + + if(!resetTime) + { + // initialize reset time + // for normal instances if no creatures are killed the instance will reset in two hours + if(entry->map_type == MAP_RAID || difficulty == DIFFICULTY_HEROIC) + resetTime = GetResetTimeFor(mapId); + else + { + resetTime = time(NULL) + 2 * HOUR; + // normally this will be removed soon after in InstanceMap::Add, prevent error + ScheduleReset(true, resetTime, InstResetEvent(0, mapId, instanceId)); + } + } + + sLog.outDebug("InstanceSaveManager::AddInstanceSave: mapid = %d, instanceid = %d", mapId, instanceId); + + save = new InstanceSave(mapId, instanceId, difficulty, resetTime, canReset); + if(!load) save->SaveToDB(); + + m_instanceSaveById[instanceId] = save; + return save; +} + +InstanceSave *InstanceSaveManager::GetInstanceSave(uint32 InstanceId) +{ + InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(InstanceId); + return itr != m_instanceSaveById.end() ? itr->second : NULL; +} + +void InstanceSaveManager::DeleteInstanceFromDB(uint32 instanceid) +{ + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'", instanceid); + CharacterDatabase.PExecute("DELETE FROM character_instance WHERE instance = '%u'", instanceid); + CharacterDatabase.PExecute("DELETE FROM group_instance WHERE instance = '%u'", instanceid); + CharacterDatabase.CommitTransaction(); + // respawn times should be deleted only when the map gets unloaded +} + +void InstanceSaveManager::RemoveInstanceSave(uint32 InstanceId) +{ + InstanceSaveHashMap::iterator itr = m_instanceSaveById.find( InstanceId ); + if(itr != m_instanceSaveById.end()) + { + // save the resettime for normal instances only when they get unloaded + if(time_t resettime = itr->second->GetResetTimeForDB()) + CharacterDatabase.PExecute("UPDATE instance SET resettime = '"I64FMTD"' WHERE id = '%u'", (uint64)resettime, InstanceId); + delete itr->second; + m_instanceSaveById.erase(itr); + } +} + +InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, uint8 difficulty, + time_t resetTime, bool canReset) +: m_mapid(MapId), m_instanceid(InstanceId), m_resetTime(resetTime), + m_difficulty(difficulty), m_canReset(canReset) +{ +} + +InstanceSave::~InstanceSave() +{ + // the players and groups must be unbound before deleting the save + assert(m_playerList.empty() && m_groupList.empty()); +} + +/* + Called from AddInstanceSave +*/ +void InstanceSave::SaveToDB() +{ + // save instance data too + std::string data; + + Map *map = MapManager::Instance().FindMap(m_instanceid, GetMapId()); + if(map) + { + assert(map->IsDungeon()); + InstanceData *iData = ((InstanceMap *)map)->GetInstanceData(); + if(iData && iData->Save()) + { + data = iData->Save(); + CharacterDatabase.escape_string(data); + } + } + + CharacterDatabase.PExecute("INSERT INTO instance VALUES ('%u', '%u', '"I64FMTD"', '%u', '%s')", m_instanceid, GetMapId(), (uint64)GetResetTimeForDB(), GetDifficulty(), data.c_str()); +} + +time_t InstanceSave::GetResetTimeForDB() +{ + // only save the reset time for normal instances + const MapEntry *entry = sMapStore.LookupEntry(GetMapId()); + if(!entry || entry->map_type == MAP_RAID || GetDifficulty() == DIFFICULTY_HEROIC) + return 0; + else + return GetResetTime(); +} + +// to cache or not to cache, that is the question +InstanceTemplate const* InstanceSave::GetTemplate() +{ + return objmgr.GetInstanceTemplate(m_mapid); +} + +MapEntry const* InstanceSave::GetMapEntry() +{ + return sMapStore.LookupEntry(m_mapid); +} + +void InstanceSave::DeleteFromDB() +{ + InstanceSaveManager::DeleteInstanceFromDB(GetInstanceId()); +} + +/* true if the instance save is still valid */ +bool InstanceSave::UnloadIfEmpty() +{ + if(m_playerList.empty() && m_groupList.empty()) + { + if(!sInstanceSaveManager.lock_instLists) + sInstanceSaveManager.RemoveInstanceSave(GetInstanceId()); + return false; + } + else + return true; +} + +void InstanceSaveManager::_DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...) +{ + Tokens fieldTokens = StrSplit(fields, ", "); + assert(fieldTokens.size() != 0); + + va_list ap; + char szQueryTail [MAX_QUERY_LEN]; + va_start(ap, queryTail); + int res = vsnprintf( szQueryTail, MAX_QUERY_LEN, queryTail, ap ); + va_end(ap); + + QueryResult *result = db.PQuery("SELECT %s FROM %s %s", fields, table, szQueryTail); + if(result) + { + do + { + Field *fields = result->Fetch(); + std::ostringstream ss; + for(size_t i = 0; i < fieldTokens.size(); i++) + { + std::string fieldValue = fields[i].GetCppString(); + db.escape_string(fieldValue); + ss << (i != 0 ? " AND " : "") << fieldTokens[i] << " = '" << fieldValue << "'"; + } + db.DirectPExecute("DELETE FROM %s WHERE %s", table, ss.str().c_str()); + } while (result->NextRow()); + delete result; + } +} + +void InstanceSaveManager::CleanupInstances() +{ + uint64 now = (uint64)time(NULL); + + barGoLink bar(2); + bar.step(); + + // load reset times and clean expired instances + sInstanceSaveManager.LoadResetTimes(); + + // clean character/group - instance binds with invalid group/characters + _DelHelper(CharacterDatabase, "character_instance.guid, instance", "character_instance", "LEFT JOIN characters ON character_instance.guid = characters.guid WHERE characters.guid IS NULL"); + _DelHelper(CharacterDatabase, "group_instance.leaderGuid, instance", "group_instance", "LEFT JOIN characters ON group_instance.leaderGuid = characters.guid LEFT JOIN groups ON group_instance.leaderGuid = groups.leaderGuid WHERE characters.guid IS NULL OR groups.leaderGuid IS NULL"); + + // clean instances that do not have any players or groups bound to them + _DelHelper(CharacterDatabase, "id, map, difficulty", "instance", "LEFT JOIN character_instance ON character_instance.instance = id LEFT JOIN group_instance ON group_instance.instance = id WHERE character_instance.instance IS NULL AND group_instance.instance IS NULL"); + + // clean invalid instance references in other tables + _DelHelper(CharacterDatabase, "character_instance.guid, instance", "character_instance", "LEFT JOIN instance ON character_instance.instance = instance.id WHERE instance.id IS NULL"); + _DelHelper(CharacterDatabase, "group_instance.leaderGuid, instance", "group_instance", "LEFT JOIN instance ON group_instance.instance = instance.id WHERE instance.id IS NULL"); + + // creature_respawn and gameobject_respawn are in another database + // first, obtain total instance set + std::set< uint32 > InstanceSet; + QueryResult *result = CharacterDatabase.PQuery("SELECT id FROM instance"); + if( result ) + { + do + { + Field *fields = result->Fetch(); + InstanceSet.insert(fields[0].GetUInt32()); + } + while (result->NextRow()); + delete result; + } + + // creature_respawn + result = WorldDatabase.PQuery("SELECT DISTINCT(instance) FROM creature_respawn WHERE instance <> 0"); + if( result ) + { + do + { + Field *fields = result->Fetch(); + if(InstanceSet.find(fields[0].GetUInt32()) == InstanceSet.end()) + WorldDatabase.DirectPExecute("DELETE FROM creature_respawn WHERE instance = '%u'", fields[0].GetUInt32()); + } + while (result->NextRow()); + delete result; + } + + // gameobject_respawn + result = WorldDatabase.PQuery("SELECT DISTINCT(instance) FROM gameobject_respawn WHERE instance <> 0"); + if( result ) + { + do + { + Field *fields = result->Fetch(); + if(InstanceSet.find(fields[0].GetUInt32()) == InstanceSet.end()) + WorldDatabase.DirectPExecute("DELETE FROM gameobject_respawn WHERE instance = '%u'", fields[0].GetUInt32()); + } + while (result->NextRow()); + delete result; + } + + bar.step(); + sLog.outString(); + sLog.outString( ">> Initialized %u instances", (uint32)InstanceSet.size()); +} + +void InstanceSaveManager::PackInstances() +{ + // this routine renumbers player instance associations in such a way so they start from 1 and go up + // TODO: this can be done a LOT more efficiently + + // obtain set of all associations + std::set< uint32 > InstanceSet; + + // all valid ids are in the instance table + // any associations to ids not in this table are assumed to be + // cleaned already in CleanupInstances + QueryResult *result = CharacterDatabase.PQuery("SELECT id FROM instance"); + if( result ) + { + do + { + Field *fields = result->Fetch(); + InstanceSet.insert(fields[0].GetUInt32()); + } + while (result->NextRow()); + delete result; + } + + barGoLink bar( InstanceSet.size() + 1); + bar.step(); + + uint32 InstanceNumber = 1; + // we do assume std::set is sorted properly on integer value + for (std::set< uint32 >::iterator i = InstanceSet.begin(); i != InstanceSet.end(); i++) + { + if (*i != InstanceNumber) + { + // remap instance id + WorldDatabase.PExecute("UPDATE creature_respawn SET instance = '%u' WHERE instance = '%u'", InstanceNumber, *i); + WorldDatabase.PExecute("UPDATE gameobject_respawn SET instance = '%u' WHERE instance = '%u'", InstanceNumber, *i); + CharacterDatabase.PExecute("UPDATE corpse SET instance = '%u' WHERE instance = '%u'", InstanceNumber, *i); + CharacterDatabase.PExecute("UPDATE character_instance SET instance = '%u' WHERE instance = '%u'", InstanceNumber, *i); + CharacterDatabase.PExecute("UPDATE instance SET id = '%u' WHERE id = '%u'", InstanceNumber, *i); + CharacterDatabase.PExecute("UPDATE group_instance SET instance = '%u' WHERE instance = '%u'", InstanceNumber, *i); + } + + ++InstanceNumber; + bar.step(); + } + + sLog.outString(); + sLog.outString( ">> Instance numbers remapped, next instance id is %u", InstanceNumber ); +} + +void InstanceSaveManager::LoadResetTimes() +{ + time_t now = time(NULL); + time_t today = (now / DAY) * DAY; + + // NOTE: Use DirectPExecute for tables that will be queried later + + // get the current reset times for normal instances (these may need to be updated) + // these are only kept in memory for InstanceSaves that are loaded later + // resettime = 0 in the DB for raid/heroic instances so those are skipped + typedef std::map > ResetTimeMapType; + ResetTimeMapType InstResetTime; + QueryResult *result = CharacterDatabase.PQuery("SELECT id, map, resettime FROM instance WHERE resettime > 0"); + if( result ) + { + do + { + if(uint64 resettime = (*result)[2].GetUInt64()) + { + uint32 id = (*result)[0].GetUInt32(); + uint32 mapid = (*result)[1].GetUInt32(); + InstResetTime[id] = std::pair(mapid, resettime); + } + } + while (result->NextRow()); + delete result; + + // update reset time for normal instances with the max creature respawn time + X hours + result = WorldDatabase.PQuery("SELECT MAX(respawntime), instance FROM creature_respawn WHERE instance > 0 GROUP BY instance"); + if( result ) + { + do + { + Field *fields = result->Fetch(); + uint32 instance = fields[1].GetUInt32(); + uint64 resettime = fields[0].GetUInt64() + 2 * HOUR; + ResetTimeMapType::iterator itr = InstResetTime.find(instance); + if(itr != InstResetTime.end() && itr->second.second != resettime) + { + CharacterDatabase.DirectPExecute("UPDATE instance SET resettime = '"I64FMTD"' WHERE id = '%u'", resettime, instance); + itr->second.second = resettime; + } + } + while (result->NextRow()); + delete result; + } + + // schedule the reset times + for(ResetTimeMapType::iterator itr = InstResetTime.begin(); itr != InstResetTime.end(); ++itr) + if(itr->second.second > now) + ScheduleReset(true, itr->second.second, InstResetEvent(0, itr->second.first, itr->first)); + } + + // load the global respawn times for raid/heroic instances + uint32 diff = sWorld.getConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR; + m_resetTimeByMapId.resize(sMapStore.GetNumRows()+1); + result = CharacterDatabase.Query("SELECT mapid, resettime FROM instance_reset"); + if(result) + { + do + { + Field *fields = result->Fetch(); + uint32 mapid = fields[0].GetUInt32(); + if(!objmgr.GetInstanceTemplate(mapid)) + { + sLog.outError("InstanceSaveManager::LoadResetTimes: invalid mapid %u in instance_reset!", mapid); + CharacterDatabase.DirectPExecute("DELETE FROM instance_reset WHERE mapid = '%u'", mapid); + continue; + } + + // update the reset time if the hour in the configs changes + uint64 oldresettime = fields[1].GetUInt64(); + uint64 newresettime = (oldresettime / DAY) * DAY + diff; + if(oldresettime != newresettime) + CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"I64FMTD"' WHERE mapid = '%u'", newresettime, mapid); + + m_resetTimeByMapId[mapid] = newresettime; + } while(result->NextRow()); + delete result; + } + + // clean expired instances, references to them will be deleted in CleanupInstances + // must be done before calculating new reset times + _DelHelper(CharacterDatabase, "id, map, difficulty", "instance", "LEFT JOIN instance_reset ON mapid = map WHERE (instance.resettime < '"I64FMTD"' AND instance.resettime > '0') OR (NOT instance_reset.resettime IS NULL AND instance_reset.resettime < '"I64FMTD"')", (uint64)now, (uint64)now); + + // calculate new global reset times for expired instances and those that have never been reset yet + // add the global reset times to the priority queue + for(uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++) + { + InstanceTemplate* temp = (InstanceTemplate*)objmgr.GetInstanceTemplate(i); + if(!temp) continue; + // only raid/heroic maps have a global reset time + const MapEntry* entry = sMapStore.LookupEntry(temp->map); + if(!entry || !entry->HasResetTime()) + continue; + + uint32 period = temp->reset_delay * DAY; + assert(period != 0); + time_t t = m_resetTimeByMapId[temp->map]; + if(!t) + { + // initialize the reset time + t = today + period + diff; + CharacterDatabase.DirectPExecute("INSERT INTO instance_reset VALUES ('%u','"I64FMTD"')", i, (uint64)t); + } + + if(t < now) + { + // assume that expired instances have already been cleaned + // calculate the next reset time + t = (t / DAY) * DAY; + t += ((today - t) / period + 1) * period + diff; + CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"I64FMTD"' WHERE mapid = '%u'", (uint64)t, i); + } + + m_resetTimeByMapId[temp->map] = t; + + // schedule the global reset/warning + uint8 type = 1; + static int tim[4] = {3600, 900, 300, 60}; + for(; type < 4; type++) + if(t - tim[type-1] > now) break; + ScheduleReset(true, t - tim[type-1], InstResetEvent(type, i)); + } +} + +void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent event) +{ + if(add) m_resetTimeQueue.insert(std::pair(time, event)); + else + { + // find the event in the queue and remove it + ResetTimeQueue::iterator itr; + std::pair range; + range = m_resetTimeQueue.equal_range(time); + for(itr = range.first; itr != range.second; ++itr) + if(itr->second == event) { m_resetTimeQueue.erase(itr); return; } + // in case the reset time changed (should happen very rarely), we search the whole queue + if(itr == range.second) + { + for(itr = m_resetTimeQueue.begin(); itr != m_resetTimeQueue.end(); ++itr) + if(itr->second == event) { m_resetTimeQueue.erase(itr); return; } + if(itr == m_resetTimeQueue.end()) + sLog.outError("InstanceSaveManager::ScheduleReset: cannot cancel the reset, the event(%d,%d,%d) was not found!", event.type, event.mapid, event.instanceId); + } + } +} + +void InstanceSaveManager::Update() +{ + time_t now = time(NULL), t; + while(!m_resetTimeQueue.empty() && (t = m_resetTimeQueue.begin()->first) < now) + { + InstResetEvent &event = m_resetTimeQueue.begin()->second; + if(event.type == 0) + { + // for individual normal instances, max creature respawn + X hours + _ResetInstance(event.mapid, event.instanceId); + m_resetTimeQueue.erase(m_resetTimeQueue.begin()); + } + else + { + // global reset/warning for a certain map + time_t resetTime = GetResetTimeFor(event.mapid); + _ResetOrWarnAll(event.mapid, event.type != 4, resetTime - now); + if(event.type != 4) + { + // schedule the next warning/reset + event.type++; + static int tim[4] = {3600, 900, 300, 60}; + ScheduleReset(true, resetTime - tim[event.type-1], event); + } + m_resetTimeQueue.erase(m_resetTimeQueue.begin()); + } + } +} + +void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr) +{ + // unbind all players bound to the instance + // do not allow UnbindInstance to automatically unload the InstanceSaves + lock_instLists = true; + InstanceSave::PlayerListType &pList = itr->second->m_playerList; + while(!pList.empty()) + { + Player *player = *(pList.begin()); + player->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true); + } + InstanceSave::GroupListType &gList = itr->second->m_groupList; + while(!gList.empty()) + { + Group *group = *(gList.begin()); + group->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true); + } + m_instanceSaveById.erase(itr++); + lock_instLists = false; +} + +void InstanceSaveManager::_ResetInstance(uint32 mapid, uint32 instanceId) +{ + sLog.outDebug("InstanceSaveMgr::_ResetInstance %u, %u", mapid, instanceId); + Map *map = (MapInstanced*)MapManager::Instance().GetBaseMap(mapid); + if(!map->Instanceable()) + return; + + InstanceSaveHashMap::iterator itr = m_instanceSaveById.find(instanceId); + if(itr != m_instanceSaveById.end()) _ResetSave(itr); + DeleteInstanceFromDB(instanceId); // even if save not loaded + + Map* iMap = ((MapInstanced*)map)->FindMap(instanceId); + if(iMap && iMap->IsDungeon()) ((InstanceMap*)iMap)->Reset(INSTANCE_RESET_RESPAWN_DELAY); + else objmgr.DeleteRespawnTimeForInstance(instanceId); // even if map is not loaded +} + +void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, bool warn, uint32 timeLeft) +{ + // global reset for all instances of the given map + // note: this isn't fast but it's meant to be executed very rarely + Map *map = (MapInstanced*)MapManager::Instance().GetBaseMap(mapid); + if(!map->Instanceable()) + return; + uint64 now = (uint64)time(NULL); + + if(!warn) + { + // this is called one minute before the reset time + InstanceTemplate* temp = (InstanceTemplate*)objmgr.GetInstanceTemplate(mapid); + if(!temp || !temp->reset_delay) + { + sLog.outError("InstanceSaveManager::ResetOrWarnAll: no instance template or reset delay for map %d", mapid); + return; + } + + // remove all binds to instances of the given map + for(InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();) + { + if(itr->second->GetMapId() == mapid) + _ResetSave(itr); + else + ++itr; + } + + // delete them from the DB, even if not loaded + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE map = '%u'", mapid); + CharacterDatabase.PExecute("DELETE FROM group_instance USING group_instance LEFT JOIN instance ON group_instance.instance = id WHERE map = '%u'", mapid); + CharacterDatabase.PExecute("DELETE FROM instance WHERE map = '%u'", mapid); + CharacterDatabase.CommitTransaction(); + + // calculate the next reset time + uint32 diff = sWorld.getConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR; + uint32 period = temp->reset_delay * DAY; + uint64 next_reset = ((now + timeLeft + MINUTE) / DAY * DAY) + period + diff; + // update it in the DB + CharacterDatabase.PExecute("UPDATE instance_reset SET resettime = '"I64FMTD"' WHERE mapid = '%d'", next_reset, mapid); + } + + MapInstanced::InstancedMaps &instMaps = ((MapInstanced*)map)->GetInstancedMaps(); + MapInstanced::InstancedMaps::iterator mitr; + for(mitr = instMaps.begin(); mitr != instMaps.end(); ++mitr) + { + Map *map = mitr->second; + if(!map->IsDungeon()) continue; + if(warn) ((InstanceMap*)map)->SendResetWarnings(timeLeft); + else ((InstanceMap*)map)->Reset(INSTANCE_RESET_GLOBAL); + } + + // TODO: delete creature/gameobject respawn times even if the maps are not loaded +} + +uint32 InstanceSaveManager::GetNumBoundPlayersTotal() +{ + uint32 ret = 0; + for(InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr) + ret += itr->second->GetPlayerCount(); + return ret; +} + +uint32 InstanceSaveManager::GetNumBoundGroupsTotal() +{ + uint32 ret = 0; + for(InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end(); ++itr) + ret += itr->second->GetGroupCount(); + return ret; +} diff --git a/src/game/InstanceSaveMgr.h b/src/game/InstanceSaveMgr.h new file mode 100644 index 00000000000..961e915eb11 --- /dev/null +++ b/src/game/InstanceSaveMgr.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __InstanceSaveMgr_H +#define __InstanceSaveMgr_H + +#include "Platform/Define.h" +#include "Policies/Singleton.h" +#include "zthread/Mutex.h" +#include +#include +#include "Utilities/HashMap.h" +#include "Database/DatabaseEnv.h" + +struct InstanceTemplate; +struct MapEntry; +class Player; +class Group; + +/* + Holds the information necessary for creating a new map for an existing instance + Is referenced in three cases: + - player-instance binds for solo players (not in group) + - player-instance binds for permanent heroic/raid saves + - group-instance binds (both solo and permanent) cache the player binds for the group leader +*/ +class InstanceSave +{ + friend class InstanceSaveManager; + public: + /* Created either when: + - any new instance is being generated + - the first time a player bound to InstanceId logs in + - when a group bound to the instance is loaded */ + InstanceSave(uint16 MapId, uint32 InstanceId, uint8 difficulty, time_t resetTime, bool canReset); + + /* Unloaded when m_playerList and m_groupList become empty + or when the instance is reset */ + ~InstanceSave(); + + uint8 GetPlayerCount() { return m_playerList.size(); } + uint8 GetGroupCount() { return m_groupList.size(); } + + /* A map corresponding to the InstanceId/MapId does not always exist. + InstanceSave objects may be created on player logon but the maps are + created and loaded only when a player actually enters the instance. */ + uint32 GetInstanceId() { return m_instanceid; } + uint32 GetMapId() { return m_mapid; } + + /* Saved when the instance is generated for the first time */ + void SaveToDB(); + /* When the instance is being reset (permanently deleted) */ + void DeleteFromDB(); + + /* for normal instances this corresponds to max(creature respawn time) + X hours + for raid/heroic instances this caches the global respawn time for the map */ + time_t GetResetTime() { return m_resetTime; } + void SetResetTime(time_t resetTime) { m_resetTime = resetTime; } + time_t GetResetTimeForDB(); + + InstanceTemplate const* GetTemplate(); + MapEntry const* GetMapEntry(); + + /* online players bound to the instance (perm/solo) + does not include the members of the group unless they have permanent saves */ + void AddPlayer(Player *player) { m_playerList.push_back(player); } + bool RemovePlayer(Player *player) { m_playerList.remove(player); return UnloadIfEmpty(); } + /* all groups bound to the instance */ + void AddGroup(Group *group) { m_groupList.push_back(group); } + bool RemoveGroup(Group *group) { m_groupList.remove(group); return UnloadIfEmpty(); } + + /* instances cannot be reset (except at the global reset time) + if there are players permanently bound to it + this is cached for the case when those players are offline */ + bool CanReset() { return m_canReset; } + void SetCanReset(bool canReset) { m_canReset = canReset; } + + /* currently it is possible to omit this information from this structure + but that would depend on a lot of things that can easily change in future */ + uint8 GetDifficulty() { return m_difficulty; } + + typedef std::list PlayerListType; + typedef std::list GroupListType; + private: + bool UnloadIfEmpty(); + /* the only reason the instSave-object links are kept is because + the object-instSave links need to be broken at reset time + TODO: maybe it's enough to just store the number of players/groups */ + PlayerListType m_playerList; + GroupListType m_groupList; + time_t m_resetTime; + uint32 m_instanceid; + uint16 m_mapid; + uint8 m_difficulty; + bool m_canReset; +}; + +class MANGOS_DLL_DECL InstanceSaveManager : public MaNGOS::Singleton > +{ + friend class InstanceSave; + public: + InstanceSaveManager(); + ~InstanceSaveManager(); + + typedef std::map InstanceSaveMap; + typedef HM_NAMESPACE::hash_map InstanceSaveHashMap; + typedef std::map InstanceSaveMapMap; + + /* resetTime is a global propery of each (raid/heroic) map + all instances of that map reset at the same time */ + struct InstResetEvent + { + uint8 type; + uint16 mapid; + uint16 instanceId; + InstResetEvent(uint8 t = 0, uint16 m = 0, uint16 i = 0) : type(t), mapid(m), instanceId(i) {} + bool operator == (const InstResetEvent& e) { return e.instanceId == instanceId; } + }; + typedef std::multimap ResetTimeQueue; + typedef std::vector ResetTimeVector; + + void CleanupInstances(); + void PackInstances(); + + void LoadResetTimes(); + time_t GetResetTimeFor(uint32 mapid) { return m_resetTimeByMapId[mapid]; } + void ScheduleReset(bool add, time_t time, InstResetEvent event); + + void Update(); + + InstanceSave* AddInstanceSave(uint32 mapId, uint32 instanceId, uint8 difficulty, time_t resetTime, bool canReset, bool load = false); + void RemoveInstanceSave(uint32 InstanceId); + static void DeleteInstanceFromDB(uint32 instanceid); + + InstanceSave *GetInstanceSave(uint32 InstanceId); + + /* statistics */ + uint32 GetNumInstanceSaves() { return m_instanceSaveById.size(); } + uint32 GetNumBoundPlayersTotal(); + uint32 GetNumBoundGroupsTotal(); + + private: + void _ResetOrWarnAll(uint32 mapid, bool warn, uint32 timeleft); + void _ResetInstance(uint32 mapid, uint32 instanceId); + void _ResetSave(InstanceSaveHashMap::iterator &itr); + void _DelHelper(DatabaseType &db, const char *fields, const char *table, const char *queryTail,...); + // used during global instance resets + bool lock_instLists; + // fast lookup by instance id + InstanceSaveHashMap m_instanceSaveById; + // fast lookup for reset times + ResetTimeVector m_resetTimeByMapId; + ResetTimeQueue m_resetTimeQueue; +}; + +#define sInstanceSaveManager MaNGOS::Singleton::Instance() +#endif diff --git a/src/game/Item.cpp b/src/game/Item.cpp new file mode 100644 index 00000000000..b7e5d010773 --- /dev/null +++ b/src/game/Item.cpp @@ -0,0 +1,915 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Item.h" +#include "ObjectMgr.h" +#include "WorldPacket.h" +#include "Database/DatabaseEnv.h" +#include "ItemEnchantmentMgr.h" + +void AddItemsSetItem(Player*player,Item *item) +{ + ItemPrototype const *proto = item->GetProto(); + uint32 setid = proto->ItemSet; + + ItemSetEntry const *set = sItemSetStore.LookupEntry(setid); + + if(!set) + { + sLog.outErrorDb("Item set %u for item (id %u) not found, mods not applied.",setid,proto->ItemId); + return; + } + + if( set->required_skill_id && player->GetSkillValue(set->required_skill_id) < set->required_skill_value ) + return; + + ItemSetEffect *eff = NULL; + + for(size_t x = 0; x < player->ItemSetEff.size(); ++x) + { + if(player->ItemSetEff[x] && player->ItemSetEff[x]->setid == setid) + { + eff = player->ItemSetEff[x]; + break; + } + } + + if(!eff) + { + eff = new ItemSetEffect; + memset(eff,0,sizeof(ItemSetEffect)); + eff->setid = setid; + + size_t x = 0; + for(; x < player->ItemSetEff.size(); x++) + if(!player->ItemSetEff[x]) + break; + + if(x < player->ItemSetEff.size()) + player->ItemSetEff[x]=eff; + else + player->ItemSetEff.push_back(eff); + } + + ++eff->item_count; + + for(uint32 x=0;x<8;x++) + { + if(!set->spells [x]) + continue; + //not enough for spell + if(set->items_to_triggerspell[x] > eff->item_count) + continue; + + uint32 z=0; + for(;z<8;z++) + if(eff->spells[z] && eff->spells[z]->Id==set->spells[x]) + break; + + if(z < 8) + continue; + + //new spell + for(uint32 y=0;y<8;y++) + { + if(!eff->spells[y]) // free slot + { + SpellEntry const *spellInfo = sSpellStore.LookupEntry(set->spells[x]); + if(!spellInfo) + { + sLog.outError("WORLD: unknown spell id %u in items set %u effects", set->spells[x],setid); + break; + } + + // spell casted only if fit form requirement, in other case will casted at form change + player->ApplyEquipSpell(spellInfo,NULL,true); + eff->spells[y] = spellInfo; + break; + } + } + } +} + +void RemoveItemsSetItem(Player*player,ItemPrototype const *proto) +{ + uint32 setid = proto->ItemSet; + + ItemSetEntry const *set = sItemSetStore.LookupEntry(setid); + + if(!set) + { + sLog.outErrorDb("Item set #%u for item #%u not found, mods not removed.",setid,proto->ItemId); + return; + } + + ItemSetEffect *eff = NULL; + size_t setindex = 0; + for(;setindex < player->ItemSetEff.size(); setindex++) + { + if(player->ItemSetEff[setindex] && player->ItemSetEff[setindex]->setid == setid) + { + eff = player->ItemSetEff[setindex]; + break; + } + } + + // can be in case now enough skill requirement for set appling but set has been appliend when skill requirement not enough + if(!eff) + return; + + --eff->item_count; + + for(uint32 x=0;x<8;x++) + { + if(!set->spells[x]) + continue; + + // enough for spell + if(set->items_to_triggerspell[x] <= eff->item_count) + continue; + + for(uint32 z=0;z<8;z++) + { + if(eff->spells[z] && eff->spells[z]->Id==set->spells[x]) + { + // spell can be not active if not fit form requirement + player->ApplyEquipSpell(eff->spells[z],NULL,false); + eff->spells[z]=NULL; + break; + } + } + } + + if(!eff->item_count) //all items of a set were removed + { + assert(eff == player->ItemSetEff[setindex]); + delete eff; + player->ItemSetEff[setindex] = NULL; + } +} + +bool ItemCanGoIntoBag(ItemPrototype const *pProto, ItemPrototype const *pBagProto) +{ + if(!pProto || !pBagProto) + return false; + + switch(pBagProto->Class) + { + case ITEM_CLASS_CONTAINER: + switch(pBagProto->SubClass) + { + case ITEM_SUBCLASS_CONTAINER: + return true; + case ITEM_SUBCLASS_SOUL_CONTAINER: + if(!(pProto->BagFamily & BAG_FAMILY_MASK_SHARDS)) + return false; + return true; + case ITEM_SUBCLASS_HERB_CONTAINER: + if(!(pProto->BagFamily & BAG_FAMILY_MASK_HERBS)) + return false; + return true; + case ITEM_SUBCLASS_ENCHANTING_CONTAINER: + if(!(pProto->BagFamily & BAG_FAMILY_MASK_ENCHANTING_SUPP)) + return false; + return true; + case ITEM_SUBCLASS_MINING_CONTAINER: + if(!(pProto->BagFamily & BAG_FAMILY_MASK_MINING_SUPP)) + return false; + return true; + case ITEM_SUBCLASS_ENGINEERING_CONTAINER: + if(!(pProto->BagFamily & BAG_FAMILY_MASK_ENGINEERING_SUPP)) + return false; + return true; + case ITEM_SUBCLASS_GEM_CONTAINER: + if(!(pProto->BagFamily & BAG_FAMILY_MASK_GEMS)) + return false; + return true; + case ITEM_SUBCLASS_LEATHERWORKING_CONTAINER: + if(!(pProto->BagFamily & BAG_FAMILY_MASK_LEATHERWORKING_SUPP)) + return false; + return true; + default: + return false; + } + case ITEM_CLASS_QUIVER: + switch(pBagProto->SubClass) + { + case ITEM_SUBCLASS_QUIVER: + if(!(pProto->BagFamily & BAG_FAMILY_MASK_ARROWS)) + return false; + return true; + case ITEM_SUBCLASS_AMMO_POUCH: + if(!(pProto->BagFamily & BAG_FAMILY_MASK_BULLETS)) + return false; + return true; + default: + return false; + } + } + return false; +} + +Item::Item( ) +{ + m_objectType |= TYPEMASK_ITEM; + m_objectTypeId = TYPEID_ITEM; + // 2.3.2 - 0x18 + m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID); + + m_valuesCount = ITEM_END; + m_slot = 0; + uState = ITEM_NEW; + uQueuePos = -1; + m_container = NULL; + m_lootGenerated = false; + mb_in_trade = false; +} + +bool Item::Create( uint32 guidlow, uint32 itemid, Player const* owner) +{ + Object::_Create( guidlow, 0, HIGHGUID_ITEM ); + + SetUInt32Value(OBJECT_FIELD_ENTRY, itemid); + SetFloatValue(OBJECT_FIELD_SCALE_X, 1.0f); + + SetUInt64Value(ITEM_FIELD_OWNER, owner ? owner->GetGUID() : 0); + SetUInt64Value(ITEM_FIELD_CONTAINED, owner ? owner->GetGUID() : 0); + + ItemPrototype const *itemProto = objmgr.GetItemPrototype(itemid); + if(!itemProto) + return false; + + SetUInt32Value(ITEM_FIELD_STACK_COUNT, 1); + SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability); + SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability); + + for(int i = 0; i < 5; ++i) + SetSpellCharges(i,itemProto->Spells[i].SpellCharges); + + SetUInt32Value(ITEM_FIELD_FLAGS, itemProto->Flags); + SetUInt32Value(ITEM_FIELD_DURATION, abs(itemProto->Duration)); + + return true; +} + +void Item::UpdateDuration(Player* owner, uint32 diff) +{ + if (!GetUInt32Value(ITEM_FIELD_DURATION)) + return; + + sLog.outDebug("Item::UpdateDuration Item (Entry: %u Duration %u Diff %u)",GetEntry(),GetUInt32Value(ITEM_FIELD_DURATION),diff); + + if (GetUInt32Value(ITEM_FIELD_DURATION)<=diff) + { + owner->DestroyItem(GetBagSlot(), GetSlot(), true); + return; + } + + SetUInt32Value(ITEM_FIELD_DURATION, GetUInt32Value(ITEM_FIELD_DURATION) - diff); + SetState(ITEM_CHANGED); // save new time in database +} + +void Item::SaveToDB() +{ + uint32 guid = GetGUIDLow(); + switch (uState) + { + case ITEM_NEW: + { + CharacterDatabase.PExecute( "DELETE FROM item_instance WHERE guid = '%u'", guid ); + std::ostringstream ss; + ss << "INSERT INTO item_instance (guid,owner_guid,data) VALUES (" << guid << "," << GUID_LOPART(GetOwnerGUID()) << ",'"; + for(uint16 i = 0; i < m_valuesCount; i++ ) + ss << GetUInt32Value(i) << " "; + ss << "' )"; + CharacterDatabase.Execute( ss.str().c_str() ); + } break; + case ITEM_CHANGED: + { + std::ostringstream ss; + ss << "UPDATE item_instance SET data = '"; + for(uint16 i = 0; i < m_valuesCount; i++ ) + ss << GetUInt32Value(i) << " "; + ss << "', owner_guid = '" << GUID_LOPART(GetOwnerGUID()) << "' WHERE guid = '" << guid << "'"; + + CharacterDatabase.Execute( ss.str().c_str() ); + + if(HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) + CharacterDatabase.PExecute("UPDATE character_gifts SET guid = '%u' WHERE item_guid = '%u'", GUID_LOPART(GetOwnerGUID()),GetGUIDLow()); + } break; + case ITEM_REMOVED: + { + if (GetUInt32Value(ITEM_FIELD_ITEM_TEXT_ID) > 0 ) + CharacterDatabase.PExecute("DELETE FROM item_text WHERE id = '%u'", GetUInt32Value(ITEM_FIELD_ITEM_TEXT_ID)); + CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", guid); + if(HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) + CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", GetGUIDLow()); + delete this; + return; + } + case ITEM_UNCHANGED: + break; + } + SetState(ITEM_UNCHANGED); +} + +bool Item::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result) +{ + // create item before any checks for store correct guid + // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB + Object::_Create(guid, 0, HIGHGUID_ITEM); + + bool delete_result = false; + if(!result) + { + result = CharacterDatabase.PQuery("SELECT data FROM item_instance WHERE guid = '%u'", guid); + delete_result = true; + } + + if (!result) + { + sLog.outError("ERROR: Item (GUID: %u owner: %u) not found in table `item_instance`, can't load. ",guid,GUID_LOPART(owner_guid)); + return false; + } + + Field *fields = result->Fetch(); + + if(!LoadValues(fields[0].GetString())) + { + sLog.outError("ERROR: Item #%d have broken data in `data` field. Can't be loaded.",guid); + if (delete_result) delete result; + return false; + } + + bool need_save = false; // need explicit save data at load fixes + + // overwrite possible wrong/corrupted guid + uint64 new_item_guid = MAKE_NEW_GUID(guid,0, HIGHGUID_ITEM); + if(GetUInt64Value(OBJECT_FIELD_GUID) != new_item_guid) + { + SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid,0, HIGHGUID_ITEM)); + need_save = true; + } + + if (delete_result) delete result; + + ItemPrototype const* proto = GetProto(); + if(!proto) + return false; + + // recalculate suffix factor + if(GetItemRandomPropertyId() < 0) + { + if(UpdateItemSuffixFactor()) + need_save = true; + } + + // Remove bind flag for items vs NO_BIND set + if (IsSoulBound() && proto->Bonding == NO_BIND) + { + ApplyModFlag(ITEM_FIELD_FLAGS,ITEM_FLAGS_BINDED, false); + need_save = true; + } + + // update duration if need, and remove if not need + if((proto->Duration==0) != (GetUInt32Value(ITEM_FIELD_DURATION)==0)) + { + SetUInt32Value(ITEM_FIELD_DURATION,abs(proto->Duration)); + need_save = true; + } + + // set correct owner + if(owner_guid != 0 && GetOwnerGUID() != owner_guid) + { + SetOwnerGUID(owner_guid); + need_save = true; + } + + if(need_save) // normal item changed state set not work at loading + { + std::ostringstream ss; + ss << "UPDATE item_instance SET data = '"; + for(uint16 i = 0; i < m_valuesCount; i++ ) + ss << GetUInt32Value(i) << " "; + ss << "', owner_guid = '" << GUID_LOPART(GetOwnerGUID()) << "' WHERE guid = '" << guid << "'"; + + CharacterDatabase.Execute( ss.str().c_str() ); + } + + return true; +} + +void Item::DeleteFromDB() +{ + CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'",GetGUIDLow()); +} + +void Item::DeleteFromInventoryDB() +{ + CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE item = '%u'",GetGUIDLow()); +} + +ItemPrototype const *Item::GetProto() const +{ + return objmgr.GetItemPrototype(GetUInt32Value(OBJECT_FIELD_ENTRY)); +} + +Player* Item::GetOwner()const +{ + return objmgr.GetPlayer(GetOwnerGUID()); +} + +uint32 Item::GetSkill() +{ + const static uint32 item_weapon_skills[MAX_ITEM_SUBCLASS_WEAPON] = + { + SKILL_AXES, SKILL_2H_AXES, SKILL_BOWS, SKILL_GUNS, SKILL_MACES, + SKILL_2H_MACES, SKILL_POLEARMS, SKILL_SWORDS, SKILL_2H_SWORDS, 0, + SKILL_STAVES, 0, 0, SKILL_UNARMED, 0, + SKILL_DAGGERS, SKILL_THROWN, SKILL_ASSASSINATION, SKILL_CROSSBOWS, SKILL_WANDS, + SKILL_FISHING + }; + + const static uint32 item_armor_skills[MAX_ITEM_SUBCLASS_ARMOR] = + { + 0,SKILL_CLOTH,SKILL_LEATHER,SKILL_MAIL,SKILL_PLATE_MAIL,0,SKILL_SHIELD,0,0,0 + }; + + ItemPrototype const* proto = GetProto(); + + switch (proto->Class) + { + case ITEM_CLASS_WEAPON: + if( proto->SubClass >= MAX_ITEM_SUBCLASS_WEAPON ) + return 0; + else + return item_weapon_skills[proto->SubClass]; + + case ITEM_CLASS_ARMOR: + if( proto->SubClass >= MAX_ITEM_SUBCLASS_ARMOR ) + return 0; + else + return item_armor_skills[proto->SubClass]; + + default: + return 0; + } +} + +uint32 Item::GetSpell() +{ + ItemPrototype const* proto = GetProto(); + + switch (proto->Class) + { + case ITEM_CLASS_WEAPON: + switch (proto->SubClass) + { + case ITEM_SUBCLASS_WEAPON_AXE: return 196; + case ITEM_SUBCLASS_WEAPON_AXE2: return 197; + case ITEM_SUBCLASS_WEAPON_BOW: return 264; + case ITEM_SUBCLASS_WEAPON_GUN: return 266; + case ITEM_SUBCLASS_WEAPON_MACE: return 198; + case ITEM_SUBCLASS_WEAPON_MACE2: return 199; + case ITEM_SUBCLASS_WEAPON_POLEARM: return 200; + case ITEM_SUBCLASS_WEAPON_SWORD: return 201; + case ITEM_SUBCLASS_WEAPON_SWORD2: return 202; + case ITEM_SUBCLASS_WEAPON_STAFF: return 227; + case ITEM_SUBCLASS_WEAPON_DAGGER: return 1180; + case ITEM_SUBCLASS_WEAPON_THROWN: return 2567; + case ITEM_SUBCLASS_WEAPON_SPEAR: return 3386; + case ITEM_SUBCLASS_WEAPON_CROSSBOW:return 5011; + case ITEM_SUBCLASS_WEAPON_WAND: return 5009; + default: return 0; + } + case ITEM_CLASS_ARMOR: + switch(proto->SubClass) + { + case ITEM_SUBCLASS_ARMOR_CLOTH: return 9078; + case ITEM_SUBCLASS_ARMOR_LEATHER: return 9077; + case ITEM_SUBCLASS_ARMOR_MAIL: return 8737; + case ITEM_SUBCLASS_ARMOR_PLATE: return 750; + case ITEM_SUBCLASS_ARMOR_SHIELD: return 9116; + default: return 0; + } + } + return 0; +} + +int32 Item::GenerateItemRandomPropertyId(uint32 item_id) +{ + ItemPrototype const *itemProto = sItemStorage.LookupEntry(item_id); + + if(!itemProto) + return 0; + + // item must have one from this field values not null if it can have random enchantments + if((!itemProto->RandomProperty) && (!itemProto->RandomSuffix)) + return 0; + + // item can have not null only one from field values + if((itemProto->RandomProperty) && (itemProto->RandomSuffix)) + { + sLog.outErrorDb("Item template %u have RandomProperty==%u and RandomSuffix==%u, but must have one from field =0",itemProto->ItemId,itemProto->RandomProperty,itemProto->RandomSuffix); + return 0; + } + + // RandomProperty case + if(itemProto->RandomProperty) + { + uint32 randomPropId = GetItemEnchantMod(itemProto->RandomProperty); + ItemRandomPropertiesEntry const *random_id = sItemRandomPropertiesStore.LookupEntry(randomPropId); + if(!random_id) + { + sLog.outErrorDb("Enchantment id #%u used but it doesn't have records in 'ItemRandomProperties.dbc'",randomPropId); + return 0; + } + + return random_id->ID; + } + // RandomSuffix case + else + { + uint32 randomPropId = GetItemEnchantMod(itemProto->RandomSuffix); + ItemRandomSuffixEntry const *random_id = sItemRandomSuffixStore.LookupEntry(randomPropId); + if(!random_id) + { + sLog.outErrorDb("Enchantment id #%u used but it doesn't have records in sItemRandomSuffixStore.",randomPropId); + return 0; + } + + return -int32(random_id->ID); + } +} + +void Item::SetItemRandomProperties(int32 randomPropId) +{ + if(!randomPropId) + return; + + if(randomPropId > 0) + { + ItemRandomPropertiesEntry const *item_rand = sItemRandomPropertiesStore.LookupEntry(randomPropId); + if(item_rand) + { + if(GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID) != int32(item_rand->ID)) + { + SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID,item_rand->ID); + SetState(ITEM_CHANGED); + } + for(uint32 i = PROP_ENCHANTMENT_SLOT_2; i < PROP_ENCHANTMENT_SLOT_2 + 3; ++i) + SetEnchantment(EnchantmentSlot(i),item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_2],0,0); + } + } + else + { + ItemRandomSuffixEntry const *item_rand = sItemRandomSuffixStore.LookupEntry(-randomPropId); + if(item_rand) + { + if( GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID) != -int32(item_rand->ID) || + !GetItemSuffixFactor()) + { + SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID,-int32(item_rand->ID)); + UpdateItemSuffixFactor(); + SetState(ITEM_CHANGED); + } + + for(uint32 i = PROP_ENCHANTMENT_SLOT_0; i < PROP_ENCHANTMENT_SLOT_0 + 3; ++i) + SetEnchantment(EnchantmentSlot(i),item_rand->enchant_id[i - PROP_ENCHANTMENT_SLOT_0],0,0); + } + } +} + +bool Item::UpdateItemSuffixFactor() +{ + uint32 suffixFactor = GenerateEnchSuffixFactor(GetEntry()); + if(GetItemSuffixFactor()==suffixFactor) + return false; + SetUInt32Value(ITEM_FIELD_PROPERTY_SEED,suffixFactor); + return true; +} + +void Item::SetState(ItemUpdateState state, Player *forplayer) +{ + if (uState == ITEM_NEW && state == ITEM_REMOVED) + { + // pretend the item never existed + RemoveFromUpdateQueueOf(forplayer); + delete this; + return; + } + + if (state != ITEM_UNCHANGED) + { + // new items must stay in new state until saved + if (uState != ITEM_NEW) uState = state; + AddToUpdateQueueOf(forplayer); + } + else + { + // unset in queue + // the item must be removed from the queue manually + uQueuePos = -1; + uState = ITEM_UNCHANGED; + } +} + +void Item::AddToUpdateQueueOf(Player *player) +{ + if (IsInUpdateQueue()) return; + + if (!player) + { + player = GetOwner(); + if (!player) + { + sLog.outError("Item::AddToUpdateQueueOf - GetPlayer didn't find a player matching owner's guid (%u)!", GUID_LOPART(GetOwnerGUID())); + return; + } + } + + if (player->GetGUID() != GetOwnerGUID()) + { + sLog.outError("Item::AddToUpdateQueueOf - Owner's guid (%u) and player's guid (%u) don't match!", GUID_LOPART(GetOwnerGUID()), player->GetGUIDLow()); + return; + } + + if (player->m_itemUpdateQueueBlocked) return; + + player->m_itemUpdateQueue.push_back(this); + uQueuePos = player->m_itemUpdateQueue.size()-1; +} + +void Item::RemoveFromUpdateQueueOf(Player *player) +{ + if (!IsInUpdateQueue()) return; + + if (!player) + { + player = GetOwner(); + if (!player) + { + sLog.outError("Item::RemoveFromUpdateQueueOf - GetPlayer didn't find a player matching owner's guid (%u)!", GUID_LOPART(GetOwnerGUID())); + return; + } + } + + if (player->GetGUID() != GetOwnerGUID()) + { + sLog.outError("Item::RemoveFromUpdateQueueOf - Owner's guid (%u) and player's guid (%u) don't match!", GUID_LOPART(GetOwnerGUID()), player->GetGUIDLow()); + return; + } + + if (player->m_itemUpdateQueueBlocked) return; + + player->m_itemUpdateQueue[uQueuePos] = NULL; + uQueuePos = -1; +} + +uint8 Item::GetBagSlot() const +{ + return m_container ? m_container->GetSlot() : uint8(INVENTORY_SLOT_BAG_0); +} + +bool Item::IsEquipped() const +{ + return !IsInBag() && m_slot < EQUIPMENT_SLOT_END; +} + +bool Item::CanBeTraded() const +{ + if(IsSoulBound()) + return false; + if(IsBag() && (Player::IsBagPos(GetPos()) || !((Bag const*)this)->IsEmpty()) ) + return false; + + if(Player* owner = GetOwner()) + { + if(owner->CanUnequipItem(GetPos(),false) != EQUIP_ERR_OK ) + return false; + if(owner->GetLootGUID()==GetGUID()) + return false; + } + + if (IsBoundByEnchant()) + return false; + + return true; +} + +bool Item::IsBoundByEnchant() const +{ + // Check all enchants for soulbound + for(uint32 enchant_slot = PERM_ENCHANTMENT_SLOT; enchant_slot < MAX_ENCHANTMENT_SLOT; ++enchant_slot) + { + uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot)); + if(!enchant_id) + continue; + + SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!enchantEntry) + continue; + + if(enchantEntry->slot & ENCHANTMENT_CAN_SOULBOUND) + return true; + } + return false; +} + +bool Item::IsFitToSpellRequirements(SpellEntry const* spellInfo) const +{ + ItemPrototype const* proto = GetProto(); + + if (spellInfo->EquippedItemClass != -1) // -1 == any item class + { + if(spellInfo->EquippedItemClass != int32(proto->Class)) + return false; // wrong item class + + if(spellInfo->EquippedItemSubClassMask != 0) // 0 == any subclass + { + if((spellInfo->EquippedItemSubClassMask & (1 << proto->SubClass)) == 0) + return false; // subclass not present in mask + } + } + + if(spellInfo->EquippedItemInventoryTypeMask != 0) // 0 == any inventory type + { + if((spellInfo->EquippedItemInventoryTypeMask & (1 << proto->InventoryType)) == 0) + return false; // inventory type not present in mask + } + + return true; +} + +void Item::SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint32 charges) +{ + // Better lost small time at check in comparison lost time at item save to DB. + if( GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET)==id && + GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET)==duration && + GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET)==charges ) + return; + + SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET,id); + SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration); + SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges); + SetState(ITEM_CHANGED); +} + +void Item::SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration) +{ + if(GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET)==duration) + return; + + SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration); + SetState(ITEM_CHANGED); +} + +void Item::SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges) +{ + SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges); + SetState(ITEM_CHANGED); +} + +void Item::ClearEnchantment(EnchantmentSlot slot) +{ + if(!GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET)) + return; + + for(int x=0;x<3;x++) + SetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + x,0); + SetState(ITEM_CHANGED); +} + +bool Item::GemsFitSockets() const +{ + bool fits = true; + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + { + uint8 SocketColor = GetProto()->Socket[enchant_slot-SOCK_ENCHANTMENT_SLOT].Color; + + uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot)); + if(!enchant_id) + { + if(SocketColor) fits &= false; + continue; + } + + SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!enchantEntry) + { + if(SocketColor) fits &= false; + continue; + } + + uint8 GemColor = 0; + + uint32 gemid = enchantEntry->GemID; + if(gemid) + { + ItemPrototype const* gemProto = sItemStorage.LookupEntry(gemid); + if(gemProto) + { + GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties); + if(gemProperty) + GemColor = gemProperty->color; + } + } + + fits &= (GemColor & SocketColor) ? true : false; + } + return fits; +} + +uint8 Item::GetGemCountWithID(uint32 GemID) const +{ + uint8 count = 0; + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + { + uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot)); + if(!enchant_id) + continue; + + SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!enchantEntry) + continue; + + if(GemID == enchantEntry->GemID) + ++count; + } + return count; +} + +bool Item::IsLimitedToAnotherMapOrZone( uint32 cur_mapId, uint32 cur_zoneId) const +{ + ItemPrototype const* proto = GetProto(); + return proto && (proto->Map && proto->Map != cur_mapId || proto->Area && proto->Area != cur_zoneId ); +} + +// Though the client has the information in the item's data field, +// we have to send SMSG_ITEM_TIME_UPDATE to display the remaining +// time. +void Item::SendTimeUpdate(Player* owner) +{ + if (!GetUInt32Value(ITEM_FIELD_DURATION)) + return; + + WorldPacket data(SMSG_ITEM_TIME_UPDATE, (8+4)); + data << (uint64)GetGUID(); + data << (uint32)GetUInt32Value(ITEM_FIELD_DURATION); + owner->GetSession()->SendPacket(&data); +} + +Item* Item::CreateItem( uint32 item, uint32 count, Player const* player ) +{ + if ( count < 1 ) + return NULL; //don't create item at zero count + + ItemPrototype const *pProto = objmgr.GetItemPrototype( item ); + if( pProto ) + { + if ( count > pProto->Stackable ) + count = pProto->Stackable; + + assert(count !=0 && "pProto->Stackable==0 but checked at loading already"); + + Item *pItem = NewItemOrBag( pProto ); + if( pItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), item, player) ) + { + pItem->SetCount( count ); + return pItem; + } + else + delete pItem; + } + return NULL; +} + +Item* Item::CloneItem( uint32 count, Player const* player ) const +{ + Item* newItem = CreateItem( GetEntry(), count, player ); + if(!newItem) + return NULL; + + newItem->SetUInt32Value( ITEM_FIELD_CREATOR, GetUInt32Value( ITEM_FIELD_CREATOR ) ); + newItem->SetUInt32Value( ITEM_FIELD_GIFTCREATOR, GetUInt32Value( ITEM_FIELD_GIFTCREATOR ) ); + newItem->SetUInt32Value( ITEM_FIELD_FLAGS, GetUInt32Value( ITEM_FIELD_FLAGS ) ); + newItem->SetUInt32Value( ITEM_FIELD_DURATION, GetUInt32Value( ITEM_FIELD_DURATION ) ); + newItem->SetItemRandomProperties(GetItemRandomPropertyId()); + return newItem; +} diff --git a/src/game/Item.h b/src/game/Item.h new file mode 100644 index 00000000000..7379edd03f8 --- /dev/null +++ b/src/game/Item.h @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_ITEM_H +#define MANGOSSERVER_ITEM_H + +#include "Common.h" +#include "Object.h" +#include "LootMgr.h" +#include "ItemPrototype.h" + +struct SpellEntry; +class Bag; +class QueryResult; + +struct ItemSetEffect +{ + uint32 setid; + uint32 item_count; + SpellEntry const *spells[8]; +}; + +enum InventoryChangeFailure +{ + EQUIP_ERR_OK = 0, + EQUIP_ERR_CANT_EQUIP_LEVEL_I = 1, + EQUIP_ERR_ERR_CANT_EQUIP_SKILL = 2, + EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT = 3, + EQUIP_ERR_BAG_FULL = 4, + EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG = 5, + EQUIP_ERR_CANT_TRADE_EQUIP_BAGS = 6, + EQUIP_ERR_ONLY_AMMO_CAN_GO_HERE = 7, + EQUIP_ERR_NO_REQUIRED_PROFICIENCY = 8, + EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE = 9, + EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM = 10, + EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM2 = 11, + EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE2 = 12, + EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED = 13, + EQUIP_ERR_CANT_DUAL_WIELD = 14, + EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG = 15, + EQUIP_ERR_ITEM_DOESNT_GO_INTO_BAG2 = 16, + EQUIP_ERR_CANT_CARRY_MORE_OF_THIS = 17, + EQUIP_ERR_NO_EQUIPMENT_SLOT_AVAILABLE3 = 18, + EQUIP_ERR_ITEM_CANT_STACK = 19, + EQUIP_ERR_ITEM_CANT_BE_EQUIPPED = 20, + EQUIP_ERR_ITEMS_CANT_BE_SWAPPED = 21, + EQUIP_ERR_SLOT_IS_EMPTY = 22, + EQUIP_ERR_ITEM_NOT_FOUND = 23, + EQUIP_ERR_CANT_DROP_SOULBOUND = 24, + EQUIP_ERR_OUT_OF_RANGE = 25, + EQUIP_ERR_TRIED_TO_SPLIT_MORE_THAN_COUNT = 26, + EQUIP_ERR_COULDNT_SPLIT_ITEMS = 27, + EQUIP_ERR_MISSING_REAGENT = 28, + EQUIP_ERR_NOT_ENOUGH_MONEY = 29, + EQUIP_ERR_NOT_A_BAG = 30, + EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS = 31, + EQUIP_ERR_DONT_OWN_THAT_ITEM = 32, + EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER = 33, + EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT = 34, + EQUIP_ERR_TOO_FAR_AWAY_FROM_BANK = 35, + EQUIP_ERR_ITEM_LOCKED = 36, + EQUIP_ERR_YOU_ARE_STUNNED = 37, + EQUIP_ERR_YOU_ARE_DEAD = 38, + EQUIP_ERR_CANT_DO_RIGHT_NOW = 39, + EQUIP_ERR_INT_BAG_ERROR = 40, + EQUIP_ERR_CAN_EQUIP_ONLY1_QUIVER2 = 41, + EQUIP_ERR_CAN_EQUIP_ONLY1_AMMOPOUCH = 42, + EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED = 43, + EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED = 44, + EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED = 45, + EQUIP_ERR_BOUND_CANT_BE_WRAPPED = 46, + EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED = 47, + EQUIP_ERR_BAGS_CANT_BE_WRAPPED = 48, + EQUIP_ERR_ALREADY_LOOTED = 49, + EQUIP_ERR_INVENTORY_FULL = 50, + EQUIP_ERR_BANK_FULL = 51, + EQUIP_ERR_ITEM_IS_CURRENTLY_SOLD_OUT = 52, + EQUIP_ERR_BAG_FULL3 = 53, + EQUIP_ERR_ITEM_NOT_FOUND2 = 54, + EQUIP_ERR_ITEM_CANT_STACK2 = 55, + EQUIP_ERR_BAG_FULL4 = 56, + EQUIP_ERR_ITEM_SOLD_OUT = 57, + EQUIP_ERR_OBJECT_IS_BUSY = 58, + EQUIP_ERR_NONE = 59, + EQUIP_ERR_NOT_IN_COMBAT = 60, + EQUIP_ERR_NOT_WHILE_DISARMED = 61, + EQUIP_ERR_BAG_FULL6 = 62, + EQUIP_ERR_CANT_EQUIP_RANK = 63, + EQUIP_ERR_CANT_EQUIP_REPUTATION = 64, + EQUIP_ERR_TOO_MANY_SPECIAL_BAGS = 65, + EQUIP_ERR_LOOT_CANT_LOOT_THAT_NOW = 66, + EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE = 67, + EQUIP_ERR_VENDOR_MISSING_TURNINS = 68, + EQUIP_ERR_NOT_ENOUGH_HONOR_POINTS = 69, + EQUIP_ERR_NOT_ENOUGH_ARENA_POINTS = 70, + EQUIP_ERR_ITEM_MAX_COUNT_SOCKETED = 71, + EQUIP_ERR_MAIL_BOUND_ITEM = 72, + EQUIP_ERR_NO_SPLIT_WHILE_PROSPECTING = 73, + EQUIP_ERR_ITEM_MAX_COUNT_EQUIPPED_SOCKETED = 75, + EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED = 76, + EQUIP_ERR_TOO_MUCH_GOLD = 77, + EQUIP_ERR_NOT_DURING_ARENA_MATCH = 78, + EQUIP_ERR_CANNOT_TRADE_THAT = 79, + EQUIP_ERR_PERSONAL_ARENA_RATING_TOO_LOW = 80 + // probably exist more +}; + +enum BuyFailure +{ + BUY_ERR_CANT_FIND_ITEM = 0, + BUY_ERR_ITEM_ALREADY_SOLD = 1, + BUY_ERR_NOT_ENOUGHT_MONEY = 2, + BUY_ERR_SELLER_DONT_LIKE_YOU = 4, + BUY_ERR_DISTANCE_TOO_FAR = 5, + BUY_ERR_ITEM_SOLD_OUT = 7, + BUY_ERR_CANT_CARRY_MORE = 8, + BUY_ERR_RANK_REQUIRE = 11, + BUY_ERR_REPUTATION_REQUIRE = 12 +}; + +enum SellFailure +{ + SELL_ERR_CANT_FIND_ITEM = 1, + SELL_ERR_CANT_SELL_ITEM = 2, // merchant doesn't like that item + SELL_ERR_CANT_FIND_VENDOR = 3, // merchant doesn't like you + SELL_ERR_YOU_DONT_OWN_THAT_ITEM = 4, // you don't own that item + SELL_ERR_UNK = 5, // nothing appears... + SELL_ERR_ONLY_EMPTY_BAG = 6 // can only do with empty bags +}; + +// -1 from client enchantment slot number +enum EnchantmentSlot +{ + PERM_ENCHANTMENT_SLOT = 0, + TEMP_ENCHANTMENT_SLOT = 1, + SOCK_ENCHANTMENT_SLOT = 2, + SOCK_ENCHANTMENT_SLOT_2 = 3, + SOCK_ENCHANTMENT_SLOT_3 = 4, + BONUS_ENCHANTMENT_SLOT = 5, + MAX_INSPECTED_ENCHANTMENT_SLOT = 6, + + PROP_ENCHANTMENT_SLOT_0 = 6, // used with RandomSuffix + PROP_ENCHANTMENT_SLOT_1 = 7, // used with RandomSuffix + PROP_ENCHANTMENT_SLOT_2 = 8, // used with RandomSuffix and RandomProperty + PROP_ENCHANTMENT_SLOT_3 = 9, // used with RandomProperty + PROP_ENCHANTMENT_SLOT_4 = 10, // used with RandomProperty + MAX_ENCHANTMENT_SLOT = 11 +}; + +#define MAX_VISIBLE_ITEM_OFFSET 16 // 16 fields per visible item (creator(2) + enchantments(12) + properties(1) + pad(1)) + +enum EnchantmentOffset +{ + ENCHANTMENT_ID_OFFSET = 0, + ENCHANTMENT_DURATION_OFFSET = 1, + ENCHANTMENT_CHARGES_OFFSET = 2 +}; + +#define MAX_ENCHANTMENT_OFFSET 3 + +enum EnchantmentSlotMask +{ + ENCHANTMENT_CAN_SOULBOUND = 0x01, + ENCHANTMENT_UNK1 = 0x02, + ENCHANTMENT_UNK2 = 0x04, + ENCHANTMENT_UNK3 = 0x08 +}; + +enum ItemUpdateState +{ + ITEM_UNCHANGED = 0, + ITEM_CHANGED = 1, + ITEM_NEW = 2, + ITEM_REMOVED = 3 +}; + +bool ItemCanGoIntoBag(ItemPrototype const *proto, ItemPrototype const *pBagProto); + +class MANGOS_DLL_SPEC Item : public Object +{ + public: + static Item* CreateItem( uint32 item, uint32 count, Player const* player = NULL ); + Item* CloneItem( uint32 count, Player const* player = NULL ) const; + + Item ( ); + + virtual bool Create( uint32 guidlow, uint32 itemid, Player const* owner); + + ItemPrototype const* GetProto() const; + + uint64 const& GetOwnerGUID() const { return GetUInt64Value(ITEM_FIELD_OWNER); } + void SetOwnerGUID(uint64 guid) { SetUInt64Value(ITEM_FIELD_OWNER, guid); } + Player* GetOwner()const; + + void SetBinding(bool val) { ApplyModFlag(ITEM_FIELD_FLAGS,ITEM_FLAGS_BINDED,val); } + bool IsSoulBound() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BINDED); } + bool IsBindedNotWith(uint64 guid) const { return IsSoulBound() && GetOwnerGUID()!= guid; } + bool IsBoundByEnchant() const; + virtual void SaveToDB(); + virtual bool LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result = NULL); + virtual void DeleteFromDB(); + void DeleteFromInventoryDB(); + + bool IsBag() const { return GetProto()->InventoryType == INVTYPE_BAG; } + bool IsBroken() const { return GetUInt32Value(ITEM_FIELD_MAXDURABILITY) > 0 && GetUInt32Value(ITEM_FIELD_DURABILITY) == 0; } + bool CanBeTraded() const; + void SetInTrade(bool b = true) { mb_in_trade = b; } + bool IsInTrade() const { return mb_in_trade; } + + bool IsFitToSpellRequirements(SpellEntry const* spellInfo) const; + bool IsLimitedToAnotherMapOrZone( uint32 cur_mapId, uint32 cur_zoneId) const; + bool GemsFitSockets() const; + + uint32 GetEntry() const { return GetUInt32Value(OBJECT_FIELD_ENTRY); } + uint32 GetCount() const { return GetUInt32Value (ITEM_FIELD_STACK_COUNT); } + void SetCount(uint32 value) { SetUInt32Value (ITEM_FIELD_STACK_COUNT, value); } + uint32 GetMaxStackCount() const { return GetProto()->Stackable; } + uint8 GetGemCountWithID(uint32 GemID) const; + + uint8 GetSlot() const {return m_slot;} + Bag *GetContainer() { return m_container; } + uint8 GetBagSlot() const; + void SetSlot(uint8 slot) {m_slot = slot;} + uint16 GetPos() const { return uint16(GetBagSlot()) << 8 | GetSlot(); } + void SetContainer(Bag *container) { m_container = container; } + + bool IsInBag() const { return m_container != NULL; } + bool IsEquipped() const; + + uint32 GetSkill(); + uint32 GetSpell(); + + // RandomPropertyId (signed but stored as unsigned) + int32 GetItemRandomPropertyId() const { return GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID); } + uint32 GetItemSuffixFactor() const { return GetUInt32Value(ITEM_FIELD_PROPERTY_SEED); } + void SetItemRandomProperties(int32 randomPropId); + bool UpdateItemSuffixFactor(); + static int32 GenerateItemRandomPropertyId(uint32 item_id); + void SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint32 charges); + void SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration); + void SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges); + void ClearEnchantment(EnchantmentSlot slot); + uint32 GetEnchantmentId(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET);} + uint32 GetEnchantmentDuration(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET);} + uint32 GetEnchantmentCharges(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET);} + + void SendTimeUpdate(Player* owner); + void UpdateDuration(Player* owner, uint32 diff); + + // spell charges (signed but stored as unsigned) + int32 GetSpellCharges(uint8 index/*0..5*/ = 0) const { return GetInt32Value(ITEM_FIELD_SPELL_CHARGES + index); } + void SetSpellCharges(uint8 index/*0..5*/, int32 value) { SetInt32Value(ITEM_FIELD_SPELL_CHARGES + index,value); } + + Loot loot; + bool m_lootGenerated; + + // Update States + ItemUpdateState GetState() const { return uState; } + void SetState(ItemUpdateState state, Player *forplayer = NULL); + void AddToUpdateQueueOf(Player *player); + void RemoveFromUpdateQueueOf(Player *player); + bool IsInUpdateQueue() const { return uQueuePos != -1; } + uint16 GetQueuePos() const { return uQueuePos; } + void FSetState(ItemUpdateState state) // forced + { + uState = state; + } + + bool hasQuest(uint32 quest_id) const + { + ItemPrototype const *itemProto = GetProto(); + return itemProto && itemProto->StartQuest == quest_id; + } + bool hasInvolvedQuest(uint32 /*quest_id*/) const { return false; } + + private: + uint8 m_slot; + Bag *m_container; + ItemUpdateState uState; + int16 uQueuePos; + bool mb_in_trade; // true if item is currently in trade-window +}; +#endif diff --git a/src/game/ItemEnchantmentMgr.cpp b/src/game/ItemEnchantmentMgr.cpp new file mode 100644 index 00000000000..9fd2e961745 --- /dev/null +++ b/src/game/ItemEnchantmentMgr.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include "ItemEnchantmentMgr.h" +#include "Database/DatabaseEnv.h" +#include "Log.h" +#include "ObjectMgr.h" +#include "ProgressBar.h" +#include +#include +#include "Util.h" + +struct EnchStoreItem +{ + uint32 ench; + float chance; + + EnchStoreItem() + : ench(0), chance(0) {} + + EnchStoreItem(uint32 _ench, float _chance) + : ench(_ench), chance(_chance) {} +}; + +typedef std::vector EnchStoreList; +typedef HM_NAMESPACE::hash_map EnchantmentStore; + +static EnchantmentStore RandomItemEnch; + +void LoadRandomEnchantmentsTable() +{ + RandomItemEnch.clear(); // for reload case + + EnchantmentStore::iterator tab; + uint32 entry, ench; + float chance; + uint32 count = 0; + + QueryResult *result = WorldDatabase.Query("SELECT entry, ench, chance FROM item_enchantment_template"); + + if (result) + { + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + entry = fields[0].GetUInt32(); + ench = fields[1].GetUInt32(); + chance = fields[2].GetFloat(); + + if (chance > 0.000001f && chance <= 100.0f) + RandomItemEnch[entry].push_back( EnchStoreItem(ench, chance) ); + + ++count; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u Item Enchantment definitions", count ); + } + else + { + sLog.outString(); + sLog.outErrorDb( ">> Loaded 0 Item Enchantment definitions. DB table `item_enchantment_template` is empty."); + } +} + +uint32 GetItemEnchantMod(uint32 entry) +{ + if (!entry) return 0; + + EnchantmentStore::iterator tab = RandomItemEnch.find(entry); + + if (tab == RandomItemEnch.end()) + { + sLog.outErrorDb("Item RandomProperty / RandomSuffix id #%u used in `item_template` but it doesn't have records in `item_enchantment_template` table.",entry); + return 0; + } + + double dRoll = rand_chance(); + float fCount = 0; + + for(EnchStoreList::iterator ench_iter = tab->second.begin(); ench_iter != tab->second.end(); ++ench_iter) + { + fCount += ench_iter->chance; + + if (fCount > dRoll) return ench_iter->ench; + } + + //we could get here only if sum of all enchantment chances is lower than 100% + dRoll = (irand(0, (int)floor(fCount * 100) + 1)) / 100; + fCount = 0; + + for(EnchStoreList::iterator ench_iter = tab->second.begin(); ench_iter != tab->second.end(); ++ench_iter) + { + fCount += ench_iter->chance; + + if (fCount > dRoll) return ench_iter->ench; + } + + return 0; +} + +uint32 GenerateEnchSuffixFactor(uint32 item_id) +{ + ItemPrototype const *itemProto = objmgr.GetItemPrototype(item_id); + + if(!itemProto) + return 0; + if(!itemProto->RandomSuffix) + return 0; + + RandomPropertiesPointsEntry const *randomProperty = sRandomPropertiesPointsStore.LookupEntry(itemProto->ItemLevel); + if(!randomProperty) + return 0; + + uint32 suffixFactor; + switch(itemProto->InventoryType) + { + // Items of that type don`t have points + case INVTYPE_NON_EQUIP: + case INVTYPE_BAG: + case INVTYPE_TABARD: + case INVTYPE_AMMO: + case INVTYPE_QUIVER: + case INVTYPE_RELIC: + return 0; + // Select point coefficient + case INVTYPE_HEAD: + case INVTYPE_BODY: + case INVTYPE_CHEST: + case INVTYPE_LEGS: + case INVTYPE_2HWEAPON: + case INVTYPE_ROBE: + suffixFactor = 0; + break; + case INVTYPE_SHOULDERS: + case INVTYPE_WAIST: + case INVTYPE_FEET: + case INVTYPE_HANDS: + case INVTYPE_TRINKET: + suffixFactor = 1; + break; + case INVTYPE_NECK: + case INVTYPE_WRISTS: + case INVTYPE_FINGER: + case INVTYPE_SHIELD: + case INVTYPE_CLOAK: + case INVTYPE_HOLDABLE: + suffixFactor = 2; + break; + case INVTYPE_WEAPON: + case INVTYPE_WEAPONMAINHAND: + case INVTYPE_WEAPONOFFHAND: + suffixFactor = 3; + break; + case INVTYPE_RANGED: + case INVTYPE_THROWN: + case INVTYPE_RANGEDRIGHT: + suffixFactor = 4; + break; + default: + return 0; + } + // Select rare/epic modifier + switch (itemProto->Quality) + { + case ITEM_QUALITY_UNCOMMON: + return randomProperty->UncommonPropertiesPoints[suffixFactor]; + case ITEM_QUALITY_RARE: + return randomProperty->RarePropertiesPoints[suffixFactor]; + case ITEM_QUALITY_EPIC: + return randomProperty->EpicPropertiesPoints[suffixFactor]; + case ITEM_QUALITY_LEGENDARY: + case ITEM_QUALITY_ARTIFACT: + return 0; // not have random properties + default: + break; + } + return 0; +} diff --git a/src/game/ItemEnchantmentMgr.h b/src/game/ItemEnchantmentMgr.h new file mode 100644 index 00000000000..95c93cf39df --- /dev/null +++ b/src/game/ItemEnchantmentMgr.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ITEM_ENCHANTMENT_MGR_H +#define _ITEM_ENCHANTMENT_MGR_H + +#include "Common.h" + +void LoadRandomEnchantmentsTable(); +uint32 GetItemEnchantMod(uint32 entry); +uint32 GenerateEnchSuffixFactor(uint32 item_id); +#endif diff --git a/src/game/ItemHandler.cpp b/src/game/ItemHandler.cpp new file mode 100644 index 00000000000..de687ad6a09 --- /dev/null +++ b/src/game/ItemHandler.cpp @@ -0,0 +1,1221 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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(); + // load vendor items if not yet + pCreature->LoadGoods(); + + uint8 numitems = pCreature->GetItemCount(); + uint8 count = 0; + uint32 ptime = time(NULL); + uint32 diff; + + WorldPacket data( SMSG_LIST_INVENTORY, (8+1+numitems*8*4) ); + data << uint64(vendorguid); + data << uint8(numitems); + + float discountMod = _player->GetReputationPriceDiscount(pCreature); + + ItemPrototype const *pProto; + for(int i = 0; i < numitems; i++ ) + { + CreatureItem* crItem = pCreature->GetItem(i); + if( crItem ) + { + pProto = objmgr.GetItemPrototype(crItem->id); + if( pProto ) + { + if((pProto->AllowableClass & _player->getClassMask()) == 0 && pProto->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster()) + continue; + ++count; + if( crItem->incrtime != 0 && (crItem->lastincr + crItem->incrtime <= ptime) ) + { + diff = uint32((ptime - crItem->lastincr)/crItem->incrtime); + if( (crItem->count + diff * pProto->BuyCount) <= crItem->maxcount ) + crItem->count += diff * pProto->BuyCount; + else + crItem->count = crItem->maxcount; + crItem->lastincr = ptime; + } + data << uint32(count); + data << uint32(crItem->id); + data << uint32(pProto->DisplayInfoID); + data << uint32(crItem->maxcount <= 0 ? 0xFFFFFFFF : crItem->count); + + uint32 price = pProto->BuyPrice; + + // reputation discount + price = uint32(floor(pProto->BuyPrice * discountMod)); + + 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(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/ItemPrototype.h b/src/game/ItemPrototype.h new file mode 100644 index 00000000000..2325d229859 --- /dev/null +++ b/src/game/ItemPrototype.h @@ -0,0 +1,565 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _ITEMPROTOTYPE_H +#define _ITEMPROTOTYPE_H + +#include "Common.h" + +enum ItemModType +{ + ITEM_MOD_MANA = 0, + ITEM_MOD_HEALTH = 1, + ITEM_MOD_AGILITY = 3, + ITEM_MOD_STRENGTH = 4, + ITEM_MOD_INTELLECT = 5, + ITEM_MOD_SPIRIT = 6, + ITEM_MOD_STAMINA = 7, + ITEM_MOD_DEFENSE_SKILL_RATING = 12, + ITEM_MOD_DODGE_RATING = 13, + ITEM_MOD_PARRY_RATING = 14, + ITEM_MOD_BLOCK_RATING = 15, + ITEM_MOD_HIT_MELEE_RATING = 16, + ITEM_MOD_HIT_RANGED_RATING = 17, + ITEM_MOD_HIT_SPELL_RATING = 18, + ITEM_MOD_CRIT_MELEE_RATING = 19, + ITEM_MOD_CRIT_RANGED_RATING = 20, + ITEM_MOD_CRIT_SPELL_RATING = 21, + ITEM_MOD_HIT_TAKEN_MELEE_RATING = 22, + ITEM_MOD_HIT_TAKEN_RANGED_RATING = 23, + ITEM_MOD_HIT_TAKEN_SPELL_RATING = 24, + ITEM_MOD_CRIT_TAKEN_MELEE_RATING = 25, + ITEM_MOD_CRIT_TAKEN_RANGED_RATING = 26, + ITEM_MOD_CRIT_TAKEN_SPELL_RATING = 27, + ITEM_MOD_HASTE_MELEE_RATING = 28, + ITEM_MOD_HASTE_RANGED_RATING = 29, + ITEM_MOD_HASTE_SPELL_RATING = 30, + ITEM_MOD_HIT_RATING = 31, + ITEM_MOD_CRIT_RATING = 32, + ITEM_MOD_HIT_TAKEN_RATING = 33, + ITEM_MOD_CRIT_TAKEN_RATING = 34, + ITEM_MOD_RESILIENCE_RATING = 35, + ITEM_MOD_HASTE_RATING = 36, + ITEM_MOD_EXPERTISE_RATING = 37 +}; + +#define MAX_ITEM_MOD 38 + +enum ItemSpelltriggerType +{ + ITEM_SPELLTRIGGER_ON_USE = 0, // use after equip cooldown + ITEM_SPELLTRIGGER_ON_EQUIP = 1, + ITEM_SPELLTRIGGER_CHANCE_ON_HIT = 2, + ITEM_SPELLTRIGGER_SOULSTONE = 4, + ITEM_SPELLTRIGGER_ON_NO_DELAY_USE = 5, // no equip cooldown + ITEM_SPELLTRIGGER_LEARN_SPELL_ID = 6 // used in item_template.spell_2 with spell_id with SPELL_GENERIC_LEARN in spell_1 +}; + +#define MAX_ITEM_SPELLTRIGGER 7 + +enum ItemBondingType +{ + NO_BIND = 0, + BIND_WHEN_PICKED_UP = 1, + BIND_WHEN_EQUIPED = 2, + BIND_WHEN_USE = 3, + BIND_QUEST_ITEM = 4, + BIND_QUEST_ITEM1 = 5 // not used in game +}; + +#define MAX_BIND_TYPE 6 + +// masks for ITEM_FIELD_FLAGS field +enum ITEM_FLAGS +{ + ITEM_FLAGS_BINDED = 0x00000001, + ITEM_FLAGS_CONJURED = 0x00000002, + ITEM_FLAGS_OPENABLE = 0x00000004, + ITEM_FLAGS_WRAPPED = 0x00000008, + ITEM_FLAGS_WRAPPER = 0x00000200, // used or not used wrapper + ITEM_FLAGS_PARTY_LOOT = 0x00000800, // determines if item is party loot or not + ITEM_FLAGS_CHARTER = 0x00002000, // arena/guild charter + ITEM_FLAGS_UNIQUE_EQUIPPED = 0x00080000, + ITEM_FLAGS_USEABLE_IN_ARENA = 0x00200000, + ITEM_FLAGS_THROWABLE = 0x00400000, // not used in game for check trow possibility, only for item in game tooltip + ITEM_FLAGS_SPECIALUSE = 0x00800000 // last used flag in 2.3.0 +}; + +enum BAG_FAMILY_MASK +{ + BAG_FAMILY_MASK_ARROWS = 0x00000001, + BAG_FAMILY_MASK_BULLETS = 0x00000002, + BAG_FAMILY_MASK_SHARDS = 0x00000004, + BAG_FAMILY_MASK_LEATHERWORKING_SUPP = 0x00000008, + BAG_FAMILY_MASK_UNUSED = 0x00000010, // not used currently + BAG_FAMILY_MASK_HERBS = 0x00000020, + BAG_FAMILY_MASK_ENCHANTING_SUPP = 0x00000040, + BAG_FAMILY_MASK_ENGINEERING_SUPP = 0x00000080, + BAG_FAMILY_MASK_KEYS = 0x00000100, + BAG_FAMILY_MASK_GEMS = 0x00000200, + BAG_FAMILY_MASK_MINING_SUPP = 0x00000400, + BAG_FAMILY_MASK_SOULBOUND_EQUIPMENT = 0x00000800, + BAG_FAMILY_MASK_VANITY_PETS = 0x00001000 +}; + +/* TODO: Not entirely positive on need for this?? +enum SOCKET_CONTENT (); +*/ + +enum SocketColor +{ + SOCKET_COLOR_META = 1, + SOCKET_COLOR_RED = 2, + SOCKET_COLOR_YELLOW = 4, + SOCKET_COLOR_BLUE = 8 +}; + +#define SOCKET_COLOR_ALL (SOCKET_COLOR_META | SOCKET_COLOR_RED | SOCKET_COLOR_YELLOW | SOCKET_COLOR_BLUE) + +enum InventoryType +{ + INVTYPE_NON_EQUIP = 0, + INVTYPE_HEAD = 1, + INVTYPE_NECK = 2, + INVTYPE_SHOULDERS = 3, + INVTYPE_BODY = 4, + INVTYPE_CHEST = 5, + INVTYPE_WAIST = 6, + INVTYPE_LEGS = 7, + INVTYPE_FEET = 8, + INVTYPE_WRISTS = 9, + INVTYPE_HANDS = 10, + INVTYPE_FINGER = 11, + INVTYPE_TRINKET = 12, + INVTYPE_WEAPON = 13, + INVTYPE_SHIELD = 14, + INVTYPE_RANGED = 15, + INVTYPE_CLOAK = 16, + INVTYPE_2HWEAPON = 17, + INVTYPE_BAG = 18, + INVTYPE_TABARD = 19, + INVTYPE_ROBE = 20, + INVTYPE_WEAPONMAINHAND = 21, + INVTYPE_WEAPONOFFHAND = 22, + INVTYPE_HOLDABLE = 23, + INVTYPE_AMMO = 24, + INVTYPE_THROWN = 25, + INVTYPE_RANGEDRIGHT = 26, + INVTYPE_QUIVER = 27, + INVTYPE_RELIC = 28 +}; + +#define MAX_INVTYPE 29 + +enum ItemClass +{ + ITEM_CLASS_CONSUMABLE = 0, + ITEM_CLASS_CONTAINER = 1, + ITEM_CLASS_WEAPON = 2, + ITEM_CLASS_GEM = 3, + ITEM_CLASS_ARMOR = 4, + ITEM_CLASS_REAGENT = 5, + ITEM_CLASS_PROJECTILE = 6, + ITEM_CLASS_TRADE_GOODS = 7, + ITEM_CLASS_GENERIC = 8, + ITEM_CLASS_RECIPE = 9, + ITEM_CLASS_MONEY = 10, + ITEM_CLASS_QUIVER = 11, + ITEM_CLASS_QUEST = 12, + ITEM_CLASS_KEY = 13, + ITEM_CLASS_PERMANENT = 14, + ITEM_CLASS_JUNK = 15 +}; + +#define MAX_ITEM_CLASS 16 + +enum ItemSubclassConsumable +{ + ITEM_SUBCLASS_CONSUMABLE = 0, + ITEM_SUBCLASS_POTION = 1, + ITEM_SUBCLASS_ELIXIR = 2, + ITEM_SUBCLASS_FLASK = 3, + ITEM_SUBCLASS_SCROLL = 4, + ITEM_SUBCLASS_FOOD = 5, + ITEM_SUBCLASS_ITEM_ENHANCEMENT = 6, + ITEM_SUBCLASS_BANDAGE = 7, + ITEM_SUBCLASS_CONSUMABLE_OTHER = 8 +}; + +#define MAX_ITEM_SUBCLASS_CONSUMABLE 9 + +enum ItemSubclassContainer +{ + ITEM_SUBCLASS_CONTAINER = 0, + ITEM_SUBCLASS_SOUL_CONTAINER = 1, + ITEM_SUBCLASS_HERB_CONTAINER = 2, + ITEM_SUBCLASS_ENCHANTING_CONTAINER = 3, + ITEM_SUBCLASS_ENGINEERING_CONTAINER = 4, + ITEM_SUBCLASS_GEM_CONTAINER = 5, + ITEM_SUBCLASS_MINING_CONTAINER = 6, + ITEM_SUBCLASS_LEATHERWORKING_CONTAINER = 7 +}; + +#define MAX_ITEM_SUBCLASS_CONTAINER 8 + +enum ItemSubclassWeapon +{ + ITEM_SUBCLASS_WEAPON_AXE = 0, + ITEM_SUBCLASS_WEAPON_AXE2 = 1, + ITEM_SUBCLASS_WEAPON_BOW = 2, + ITEM_SUBCLASS_WEAPON_GUN = 3, + ITEM_SUBCLASS_WEAPON_MACE = 4, + ITEM_SUBCLASS_WEAPON_MACE2 = 5, + ITEM_SUBCLASS_WEAPON_POLEARM = 6, + ITEM_SUBCLASS_WEAPON_SWORD = 7, + ITEM_SUBCLASS_WEAPON_SWORD2 = 8, + ITEM_SUBCLASS_WEAPON_obsolete = 9, + ITEM_SUBCLASS_WEAPON_STAFF = 10, + ITEM_SUBCLASS_WEAPON_EXOTIC = 11, + ITEM_SUBCLASS_WEAPON_EXOTIC2 = 12, + ITEM_SUBCLASS_WEAPON_FIST = 13, + ITEM_SUBCLASS_WEAPON_MISC = 14, + ITEM_SUBCLASS_WEAPON_DAGGER = 15, + ITEM_SUBCLASS_WEAPON_THROWN = 16, + ITEM_SUBCLASS_WEAPON_SPEAR = 17, + ITEM_SUBCLASS_WEAPON_CROSSBOW = 18, + ITEM_SUBCLASS_WEAPON_WAND = 19, + ITEM_SUBCLASS_WEAPON_FISHING_POLE = 20 +}; + +#define MAX_ITEM_SUBCLASS_WEAPON 21 + +enum ItemSubclassGem +{ + ITEM_SUBCLASS_GEM_RED = 0, + ITEM_SUBCLASS_GEM_BLUE = 1, + ITEM_SUBCLASS_GEM_YELLOW = 2, + ITEM_SUBCLASS_GEM_PURPLE = 3, + ITEM_SUBCLASS_GEM_GREEN = 4, + ITEM_SUBCLASS_GEM_ORANGE = 5, + ITEM_SUBCLASS_GEM_META = 6, + ITEM_SUBCLASS_GEM_SIMPLE = 7, + ITEM_SUBCLASS_GEM_PRISMATIC = 8 +}; + +#define MAX_ITEM_SUBCLASS_GEM 9 + +enum ItemSubclassArmor +{ + ITEM_SUBCLASS_ARMOR_MISC = 0, + ITEM_SUBCLASS_ARMOR_CLOTH = 1, + ITEM_SUBCLASS_ARMOR_LEATHER = 2, + ITEM_SUBCLASS_ARMOR_MAIL = 3, + ITEM_SUBCLASS_ARMOR_PLATE = 4, + ITEM_SUBCLASS_ARMOR_BUCKLER = 5, + ITEM_SUBCLASS_ARMOR_SHIELD = 6, + ITEM_SUBCLASS_ARMOR_LIBRAM = 7, + ITEM_SUBCLASS_ARMOR_IDOL = 8, + ITEM_SUBCLASS_ARMOR_TOTEM = 9 +}; + +#define MAX_ITEM_SUBCLASS_ARMOR 10 + +enum ItemSubclassReagent +{ + ITEM_SUBCLASS_REAGENT = 0 +}; + +#define MAX_ITEM_SUBCLASS_REAGENT 1 + +enum ItemSubclassProjectile +{ + ITEM_SUBCLASS_WAND = 0, // ABS + ITEM_SUBCLASS_BOLT = 1, // ABS + ITEM_SUBCLASS_ARROW = 2, + ITEM_SUBCLASS_BULLET = 3, + ITEM_SUBCLASS_THROWN = 4 // ABS +}; + +#define MAX_ITEM_SUBCLASS_PROJECTILE 5 + +enum ItemSubclassTradeGoods +{ + ITEM_SUBCLASS_TRADE_GOODS = 0, + ITEM_SUBCLASS_PARTS = 1, + ITEM_SUBCLASS_EXPLOSIVES = 2, + ITEM_SUBCLASS_DEVICES = 3, + ITEM_SUBCLASS_JEWELCRAFTING = 4, + ITEM_SUBCLASS_CLOTH = 5, + ITEM_SUBCLASS_LEATHER = 6, + ITEM_SUBCLASS_METAL_STONE = 7, + ITEM_SUBCLASS_MEAT = 8, + ITEM_SUBCLASS_HERB = 9, + ITEM_SUBCLASS_ELEMENTAL = 10, + ITEM_SUBCLASS_TRADE_GOODS_OTHER = 11, + ITEM_SUBCLASS_ENCHANTING = 12, + ITEM_SUBCLASS_MATERIAL = 13 // Added in 2.4.2 +}; + +#define MAX_ITEM_SUBCLASS_TRADE_GOODS 14 + +enum ItemSubclassGeneric +{ + ITEM_SUBCLASS_GENERIC = 0 +}; + +#define MAX_ITEM_SUBCLASS_GENERIC 1 + +enum ItemSubclassRecipe +{ + ITEM_SUBCLASS_BOOK = 0, + ITEM_SUBCLASS_LEATHERWORKING_PATTERN = 1, + ITEM_SUBCLASS_TAILORING_PATTERN = 2, + ITEM_SUBCLASS_ENGINEERING_SCHEMATIC = 3, + ITEM_SUBCLASS_BLACKSMITHING = 4, + ITEM_SUBCLASS_COOKING_RECIPE = 5, + ITEM_SUBCLASS_ALCHEMY_RECIPE = 6, + ITEM_SUBCLASS_FIRST_AID_MANUAL = 7, + ITEM_SUBCLASS_ENCHANTING_FORMULA = 8, + ITEM_SUBCLASS_FISHING_MANUAL = 9, + ITEM_SUBCLASS_JEWELCRAFTING_RECIPE = 10 +}; + +#define MAX_ITEM_SUBCLASS_RECIPE 11 + +enum ItemSubclassMoney +{ + ITEM_SUBCLASS_MONEY = 0 +}; + +#define MAX_ITEM_SUBCLASS_MONEY 1 + +enum ItemSubclassQuiver +{ + ITEM_SUBCLASS_QUIVER0 = 0, // ABS + ITEM_SUBCLASS_QUIVER1 = 1, // ABS + ITEM_SUBCLASS_QUIVER = 2, + ITEM_SUBCLASS_AMMO_POUCH = 3 +}; + +#define MAX_ITEM_SUBCLASS_QUIVER 4 + +enum ItemSubclassQuest +{ + ITEM_SUBCLASS_QUEST = 0 +}; + +#define MAX_ITEM_SUBCLASS_QUEST 1 + +enum ItemSubclassKey +{ + ITEM_SUBCLASS_KEY = 0, + ITEM_SUBCLASS_LOCKPICK = 1 +}; + +#define MAX_ITEM_SUBCLASS_KEY 2 + +enum ItemSubclassPermanent +{ + ITEM_SUBCLASS_PERMANENT = 0 +}; + +#define MAX_ITEM_SUBCLASS_PERMANENT 1 + +enum ItemSubclassJunk +{ + ITEM_SUBCLASS_JUNK = 0, + ITEM_SUBCLASS_JUNK_REAGENT = 1, + ITEM_SUBCLASS_JUNK_PET = 2, + ITEM_SUBCLASS_JUNK_HOLIDAY = 3, + ITEM_SUBCLASS_JUNK_OTHER = 4, + ITEM_SUBCLASS_JUNK_MOUNT = 5 +}; + +#define MAX_ITEM_SUBCLASS_JUNK 6 + +const uint32 MaxItemSubclassValues[MAX_ITEM_CLASS] = +{ + MAX_ITEM_SUBCLASS_CONSUMABLE, + MAX_ITEM_SUBCLASS_CONTAINER, + MAX_ITEM_SUBCLASS_WEAPON, + MAX_ITEM_SUBCLASS_GEM, + MAX_ITEM_SUBCLASS_ARMOR, + MAX_ITEM_SUBCLASS_REAGENT, + MAX_ITEM_SUBCLASS_PROJECTILE, + MAX_ITEM_SUBCLASS_TRADE_GOODS, + MAX_ITEM_SUBCLASS_GENERIC, + MAX_ITEM_SUBCLASS_RECIPE, + MAX_ITEM_SUBCLASS_MONEY, + MAX_ITEM_SUBCLASS_QUIVER, + MAX_ITEM_SUBCLASS_QUEST, + MAX_ITEM_SUBCLASS_KEY, + MAX_ITEM_SUBCLASS_PERMANENT, + MAX_ITEM_SUBCLASS_JUNK +}; + +inline uint8 ItemSubClassToDurabilityMultiplierId(uint32 ItemClass, uint32 ItemSubClass) +{ + switch(ItemClass) + { + case ITEM_CLASS_WEAPON: return ItemSubClass; + case ITEM_CLASS_ARMOR: return ItemSubClass + 21; + } + return 0; +} + +// 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 + +struct _Damage +{ + float DamageMin; + float DamageMax; + uint32 DamageType; // id from Resistances.dbc + +}; + +struct _ItemStat +{ + uint32 ItemStatType; + int32 ItemStatValue; + +}; +struct _Spell +{ + uint32 SpellId; // id from Spell.dbc + uint32 SpellTrigger; + int32 SpellCharges; + float SpellPPMRate; + int32 SpellCooldown; + uint32 SpellCategory; // id from SpellCategory.dbc + int32 SpellCategoryCooldown; + +}; + +struct _Socket +{ + uint32 Color; + uint32 Content; +}; + +struct ItemPrototype +{ + uint32 ItemId; + uint32 Class; // id from ItemClass.dbc + uint32 SubClass; // id from ItemSubClass.dbc + uint32 Unk0; + char* Name1; + uint32 DisplayInfoID; // id from ItemDisplayInfo.dbc + uint32 Quality; + uint32 Flags; + uint32 BuyCount; + uint32 BuyPrice; + uint32 SellPrice; + uint32 InventoryType; + uint32 AllowableClass; + uint32 AllowableRace; + uint32 ItemLevel; + uint32 RequiredLevel; + uint32 RequiredSkill; // id from SkillLine.dbc + uint32 RequiredSkillRank; + uint32 RequiredSpell; // id from Spell.dbc + uint32 RequiredHonorRank; + uint32 RequiredCityRank; + uint32 RequiredReputationFaction; // id from Faction.dbc + uint32 RequiredReputationRank; + uint32 MaxCount; + uint32 Stackable; + uint32 ContainerSlots; + _ItemStat ItemStat[10]; + _Damage Damage[5]; + uint32 Armor; + uint32 HolyRes; + uint32 FireRes; + uint32 NatureRes; + uint32 FrostRes; + uint32 ShadowRes; + uint32 ArcaneRes; + uint32 Delay; + uint32 Ammo_type; + float RangedModRange; + + _Spell Spells[5]; + uint32 Bonding; + char* Description; + uint32 PageText; + uint32 LanguageID; + uint32 PageMaterial; + uint32 StartQuest; // id from QuestCache.wdb + uint32 LockID; + uint32 Material; // id from Material.dbc + uint32 Sheath; + uint32 RandomProperty; // id from ItemRandomProperties.dbc + uint32 RandomSuffix; // id from ItemRandomSuffix.dbc + uint32 Block; + uint32 ItemSet; // id from ItemSet.dbc + uint32 MaxDurability; + uint32 Area; // id from AreaTable.dbc + uint32 Map; // id from Map.dbc + uint32 BagFamily; // id from ItemBagFamily.dbc + uint32 TotemCategory; // id from TotemCategory.dbc + _Socket Socket[3]; + uint32 socketBonus; // id from SpellItemEnchantment.dbc + uint32 GemProperties; // id from GemProperties.dbc + uint32 RequiredDisenchantSkill; + float ArmorDamageModifier; + char* ScriptName; + uint32 DisenchantID; + uint32 FoodType; + uint32 MinMoneyLoot; + uint32 MaxMoneyLoot; + int32 Duration; // negative = realtime, positive = ingame time + + // helpers + bool CanChangeEquipStateInCombat() const + { + switch(InventoryType) + { + case INVTYPE_RELIC: + case INVTYPE_SHIELD: + return true; + } + + switch(Class) + { + case ITEM_CLASS_WEAPON: + case ITEM_CLASS_PROJECTILE: + return true; + } + + return false; + } +}; + +struct ItemLocale +{ + std::vector Name; + std::vector Description; +}; + +// 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 +#endif diff --git a/src/game/LFGHandler.cpp b/src/game/LFGHandler.cpp new file mode 100644 index 00000000000..df87b51de89 --- /dev/null +++ b/src/game/LFGHandler.cpp @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "WorldSession.h" +#include "Log.h" +#include "Database/DatabaseEnv.h" +#include "Player.h" +#include "WorldPacket.h" +#include "ObjectMgr.h" +#include "World.h" + +static void AttemptJoin(Player* _player) +{ + // skip not can autojoin cases and player group case + if(!_player->m_lookingForGroup.canAutoJoin() || _player->GetGroup()) + return; + + //TODO: Guard Player Map + HashMapHolder::MapType const& players = ObjectAccessor::Instance().GetPlayers(); + for(HashMapHolder::MapType::const_iterator iter = players.begin(); iter != players.end(); ++iter) + { + Player *plr = iter->second; + + // skip enemies and self + if(!plr || plr==_player || plr->GetTeam() != _player->GetTeam()) + continue; + + // skip not auto add, not group leader cases + if(!plr->GetSession()->LookingForGroup_auto_add || plr->GetGroup() && plr->GetGroup()->GetLeaderGUID()!=plr->GetGUID()) + continue; + + // skip non auto-join or empty slots, or non compatible slots + if(!plr->m_lookingForGroup.more.canAutoJoin() || !_player->m_lookingForGroup.HaveInSlot(plr->m_lookingForGroup.more)) + continue; + + // attempt create group, or skip + if(!plr->GetGroup()) + { + Group* group = new Group; + if(!group->Create(plr->GetGUID(), plr->GetName())) + { + delete group; + continue; + } + + objmgr.AddGroup(group); + } + + // stop at success join + if(plr->GetGroup()->AddMember(_player->GetGUID(), _player->GetName())) + { + if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && _player->GetSession()->GetSecurity() == SEC_PLAYER ) + _player->LeaveLFGChannel(); + break; + } + // full + else + { + if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && plr->GetSession()->GetSecurity() == SEC_PLAYER ) + plr->LeaveLFGChannel(); + } + } +} + +static void AttemptAddMore(Player* _player) +{ + // skip not group leader case + if(_player->GetGroup() && _player->GetGroup()->GetLeaderGUID()!=_player->GetGUID()) + return; + + if(!_player->m_lookingForGroup.more.canAutoJoin()) + return; + + //TODO: Guard Player map + HashMapHolder::MapType const& players = ObjectAccessor::Instance().GetPlayers(); + for(HashMapHolder::MapType::const_iterator iter = players.begin(); iter != players.end(); ++iter) + { + Player *plr = iter->second; + + // skip enemies and self + if(!plr || plr==_player || plr->GetTeam() != _player->GetTeam()) + continue; + + // skip not auto join or in group + if(!plr->GetSession()->LookingForGroup_auto_join || plr->GetGroup() ) + continue; + + if(!plr->m_lookingForGroup.HaveInSlot(_player->m_lookingForGroup.more)) + continue; + + // attempt create group if need, or stop attempts + if(!_player->GetGroup()) + { + Group* group = new Group; + if(!group->Create(_player->GetGUID(), _player->GetName())) + { + delete group; + return; // cann't create group (??) + } + + objmgr.AddGroup(group); + } + + // stop at join fail (full) + if(!_player->GetGroup()->AddMember(plr->GetGUID(), plr->GetName()) ) + { + if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && _player->GetSession()->GetSecurity() == SEC_PLAYER ) + _player->LeaveLFGChannel(); + + break; + } + + // joined + if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && plr->GetSession()->GetSecurity() == SEC_PLAYER ) + plr->LeaveLFGChannel(); + + // and group full + if(_player->GetGroup()->IsFull() ) + { + if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && _player->GetSession()->GetSecurity() == SEC_PLAYER ) + _player->LeaveLFGChannel(); + + break; + } + } +} + +void WorldSession::HandleLfgAutoJoinOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug("CMSG_SET_LFG_AUTO_JOIN"); + LookingForGroup_auto_join = true; + + if(!_player) // needed because STATUS_AUTHED + return; + + AttemptJoin(_player); +} + +void WorldSession::HandleLfgCancelAutoJoinOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug("CMSG_UNSET_LFG_AUTO_JOIN"); + LookingForGroup_auto_join = false; +} + +void WorldSession::HandleLfmAutoAddMembersOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug("CMSG_SET_LFM_AUTOADD"); + LookingForGroup_auto_add = true; + + if(!_player) // needed because STATUS_AUTHED + return; + + AttemptAddMore(_player); +} + +void WorldSession::HandleLfmCancelAutoAddmembersOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug("CMSG_UNSET_LFM_AUTOADD"); + LookingForGroup_auto_add = false; +} + +void WorldSession::HandleLfgClearOpcode( WorldPacket & /*recv_data */ ) +{ + sLog.outDebug("CMSG_LOOKING_FOR_GROUP_CLEAR"); + + for(int i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i) + _player->m_lookingForGroup.slots[i].Clear(); + + if( sWorld.getConfig(CONFIG_RESTRICTED_LFG_CHANNEL) && _player->GetSession()->GetSecurity() == SEC_PLAYER ) + _player->LeaveLFGChannel(); +} + +void WorldSession::HandleLfmSetNoneOpcode( WorldPacket & /*recv_data */) +{ + sLog.outDebug("CMSG_SET_LOOKING_FOR_NONE"); + + _player->m_lookingForGroup.more.Clear(); +} + +void WorldSession::HandleLfmSetOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4); + + sLog.outDebug("CMSG_SET_LOOKING_FOR_MORE"); + //recv_data.hexlike(); + uint32 temp, entry, type; + + recv_data >> temp; + + entry = ( temp & 0xFFFF); + type = ( (temp >> 24) & 0xFFFF); + + _player->m_lookingForGroup.more.Set(entry,type); + sLog.outDebug("LFM set: temp %u, zone %u, type %u", temp, entry, type); + + if(LookingForGroup_auto_add) + AttemptAddMore(_player); + + SendLfgResult(type, entry, 1); +} + +void WorldSession::HandleLfgSetCommentOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,1); + + sLog.outDebug("CMSG_SET_COMMENTARY"); + //recv_data.hexlike(); + + std::string comment; + recv_data >> comment; + sLog.outDebug("LFG comment %s", comment.c_str()); + + _player->m_lookingForGroup.comment = comment; +} + +void WorldSession::HandleLookingForGroup(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data,4+4+4); + + sLog.outDebug("MSG_LOOKING_FOR_GROUP"); + //recv_data.hexlike(); + uint32 type, entry, unk; + + recv_data >> type >> entry >> unk; + sLog.outDebug("MSG_LOOKING_FOR_GROUP: type %u, entry %u, unk %u", type, entry, unk); + + if(LookingForGroup_auto_add) + AttemptAddMore(_player); + + if(LookingForGroup_auto_join) + AttemptJoin(_player); + + SendLfgResult(type, entry, 0); +} + +void WorldSession::SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type) +{ + uint32 number = 0; + + // start preper packet; + WorldPacket data(MSG_LOOKING_FOR_GROUP); + data << uint32(type); // type + data << uint32(entry); // entry from LFGDungeons.dbc + data << uint32(0); // count, placeholder + data << uint32(0); // count again, strange, placeholder + + //TODO: Guard Player map + HashMapHolder::MapType const& players = ObjectAccessor::Instance().GetPlayers(); + for(HashMapHolder::MapType::const_iterator iter = players.begin(); iter != players.end(); ++iter) + { + Player *plr = iter->second; + + if(!plr || plr->GetTeam() != _player->GetTeam()) + continue; + + if(!plr->m_lookingForGroup.HaveInSlot(entry,type)) + continue; + + ++number; + + data.append(plr->GetPackGUID()); // packed guid + data << plr->getLevel(); // level + data << plr->GetZoneId(); // current zone + data << lfg_type; // 0x00 - LFG, 0x01 - LFM + + for(uint8 j = 0; j < MAX_LOOKING_FOR_GROUP_SLOT; ++j) + { + data << uint32( plr->m_lookingForGroup.slots[j].entry | (plr->m_lookingForGroup.slots[j].type << 24) ); + } + data << plr->m_lookingForGroup.comment; + + Group *group = plr->GetGroup(); + if(group) + { + data << group->GetMembersCount()-1; // count of group members without group leader + for(GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *member = itr->getSource(); + if(member && member->GetGUID() != plr->GetGUID()) + { + data.append(member->GetPackGUID()); // packed guid + data << member->getLevel(); // player level + } + } + } + else + { + data << uint32(0x00); + } + } + + // fill count placeholders + data.put(4+4, number); + data.put(4+4+4,number); + + SendPacket(&data); +} + +void WorldSession::HandleSetLfgOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+4); + + sLog.outDebug("CMSG_SET_LOOKING_FOR_GROUP"); + //recv_data.hexlike(); + uint32 slot, temp, entry, type; + + recv_data >> slot >> temp; + + entry = ( temp & 0xFFFF); + type = ( (temp >> 24) & 0xFFFF); + + if(slot >= MAX_LOOKING_FOR_GROUP_SLOT) + return; + + _player->m_lookingForGroup.slots[slot].Set(entry,type); + sLog.outDebug("LFG set: looknumber %u, temp %X, type %u, entry %u", slot, temp, type, entry); + + if(LookingForGroup_auto_join) + AttemptJoin(_player); + + SendLfgResult(type, entry, 0); +} diff --git a/src/game/Language.h b/src/game/Language.h new file mode 100644 index 00000000000..bb36bffcee6 --- /dev/null +++ b/src/game/Language.h @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __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, + // Room for more level 0 + + // 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_NOREQUEST = 166, + LANG_COMMAND_TELE_NOLOCATION = 167, + LANG_COMMAND_TELE_LOCATION = 168, + + LANG_MAIL_SENT = 169, + LANG_SOUND_NOT_EXIST = 170, + // Room for more level 1 + + // 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, + + // Room for more level 2 + + // 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, + LANG_COMMAND_TP_DELETEERR = 466, + + 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_QUEUE_ANNOUNCE_SELF = 711, + LANG_BG_QUEUE_ANNOUNCE_WORLD = 712, +}; +#endif + +/* NOT USED VALUES +// alliance ranks +#define LANG_ALI_PRIVATE "Private " +#define LANG_ALI_CORPORAL "Corporal " +#define LANG_ALI_SERGEANT "Sergeant " +#define LANG_ALI_MASTER_SERGEANT "Master Sergeant " +#define LANG_ALI_SERGEANT_MAJOR "Sergeant Major " +#define LANG_ALI_KNIGHT "Knight " +#define LANG_ALI_KNIGHT_LIEUTENANT "Knight-Lieutenant " +#define LANG_ALI_KNIGHT_CAPTAIN "Knight-Captain " +#define LANG_ALI_KNIGHT_CHAMPION "Knight-Champion " +#define LANG_ALI_LIEUTENANT_COMMANDER "Lieutenant Commander " +#define LANG_ALI_COMMANDER "Commander " +#define LANG_ALI_MARSHAL "Marshal " +#define LANG_ALI_FIELD_MARSHAL "Field Marshal " +#define LANG_ALI_GRAND_MARSHAL "Grand Marshal " +#define LANG_ALI_GAME_MASTER "Game Master " + +// horde ranks +#define LANG_HRD_SCOUT "Scout " +#define LANG_HRD_GRUNT "Grunt " +#define LANG_HRD_SERGEANT "Sergeant " +#define LANG_HRD_SENIOR_SERGEANT "Senior Sergeant " +#define LANG_HRD_FIRST_SERGEANT "First Sergeant " +#define LANG_HRD_STONE_GUARD "Stone Guard " +#define LANG_HRD_BLOOD_GUARD "Blood Guard " +#define LANG_HRD_LEGIONNARE "Legionnaire " +#define LANG_HRD_CENTURION "Centurion " +#define LANG_HRD_CHAMPION "Champion " +#define LANG_HRD_LIEUTENANT_GENERAL "Lieutenant General " +#define LANG_HRD_GENERAL "General " +#define LANG_HRD_WARLORD "Warlord " +#define LANG_HRD_HIGH_WARLORD "High Warlord " +#define LANG_HRD_GAME_MASTER "Game Master " + +#define LANG_NO_RANK "No rank " +#define LANG_RANK "%s (Rank %u)" +#define LANG_HONOR_TODAY "Today: [Honorable kills: |c0000ff00%u|r] [Dishonorable kills: |c00ff0000%u|r]" +#define LANG_HONOR_YESTERDAY "Yesterday: [Kills: |c0000ff00%u|r] [Honor: %u]" +#define LANG_HONOR_THIS_WEEK "This week: [Kills: |c0000ff00%u|r] [Honor: %u]" +#define LANG_HONOR_LAST_WEEK "Last week: [Kills: |c0000ff00%u|r] [Honor: %u] [Standing: %u]" +#define LANG_HONOR_LIFE "Lifetime: [Honorable kills: |c0000ff00%u|r] [Dishonorable kills: |c00ff0000%u|r] [Highest rank %u: %s]" + +// level 2 +#define LANG_ADD_OBJ "AddObject at Chat.cpp" //log +#define LANG_DEMORPHED "Demorphed %s" //log + +// level 3 +#define LANG_SPAWNING_SPIRIT_HEAL "Spawning spirit healers\n" +#define LANG_NO_SPIRIT_HEAL_DB "No spirit healers in database, exiting." + +#define LANG_ADD_OBJ_LV3 "AddObject at Level3.cpp line 1176" + +*/ diff --git a/src/game/Level0.cpp b/src/game/Level0.cpp new file mode 100644 index 00000000000..5130b158360 --- /dev/null +++ b/src/game/Level0.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "Player.h" +#include "Opcodes.h" +#include "Chat.h" +#include "MapManager.h" +#include "ObjectAccessor.h" +#include "Language.h" +#include "AccountMgr.h" +#include "SystemConfig.h" +#include "Util.h" + +bool ChatHandler::HandleHelpCommand(const char* args) +{ + if(!*args) + return false; + + char* cmd = strtok((char*)args, " "); + if(!cmd) + return false; + + if(!ShowHelpForCommand(getCommandTable(), cmd)) + SendSysMessage(LANG_NO_HELP_CMD); + + return true; +} + +bool ChatHandler::HandleCommandsCommand(const char* args) +{ + ShowHelpForCommand(getCommandTable(), ""); + return true; +} + +bool ChatHandler::HandleAcctCommand(const char* /*args*/) +{ + uint32 gmlevel = m_session->GetSecurity(); + PSendSysMessage(LANG_ACCOUNT_LEVEL, gmlevel); + return true; +} + +bool ChatHandler::HandleStartCommand(const char* /*args*/) +{ + Player *chr = m_session->GetPlayer(); + + if(chr->isInFlight()) + { + SendSysMessage(LANG_YOU_IN_FLIGHT); + SetSentErrorMessage(true); + return false; + } + + if(chr->isInCombat()) + { + SendSysMessage(LANG_YOU_IN_COMBAT); + SetSentErrorMessage(true); + return false; + } + + // cast spell Stuck + chr->CastSpell(chr,7355,false); + return true; +} + +bool ChatHandler::HandleInfoCommand(const char* /*args*/) +{ + uint32 activeClientsNum = sWorld.GetActiveSessionCount(); + uint32 queuedClientsNum = sWorld.GetQueuedSessionCount(); + uint32 maxActiveClientsNum = sWorld.GetMaxActiveSessionCount(); + uint32 maxQueuedClientsNum = sWorld.GetMaxQueuedSessionCount(); + std::string str = secsToTimeString(sWorld.GetUptime()); + + PSendSysMessage(_FULLVERSION); + PSendSysMessage(LANG_CONNECTED_USERS, activeClientsNum, maxActiveClientsNum, queuedClientsNum, maxQueuedClientsNum); + PSendSysMessage(LANG_UPTIME, str.c_str()); + + return true; +} + +bool ChatHandler::HandleDismountCommand(const char* /*args*/) +{ + //If player is not mounted, so go out :) + if (!m_session->GetPlayer( )->IsMounted()) + { + SendSysMessage(LANG_CHAR_NON_MOUNTED); + SetSentErrorMessage(true); + return false; + } + + if(m_session->GetPlayer( )->isInFlight()) + { + SendSysMessage(LANG_YOU_IN_FLIGHT); + SetSentErrorMessage(true); + return false; + } + + m_session->GetPlayer()->Unmount(); + m_session->GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + return true; +} + +bool ChatHandler::HandleSaveCommand(const char* /*args*/) +{ + Player *player=m_session->GetPlayer(); + + // save GM account without delay and output message (testing, etc) + if(m_session->GetSecurity()) + { + player->SaveToDB(); + SendSysMessage(LANG_PLAYER_SAVED); + return true; + } + + // save or plan save after 20 sec (logout delay) if current next save time more this value and _not_ output any messages to prevent cheat planning + uint32 save_interval = sWorld.getConfig(CONFIG_INTERVAL_SAVE); + if(save_interval==0 || save_interval > 20*1000 && player->GetSaveTimer() <= save_interval - 20*1000) + player->SaveToDB(); + + return true; +} + +bool ChatHandler::HandleGMListCommand(const char* /*args*/) +{ + bool first = true; + + HashMapHolder::MapType &m = HashMapHolder::GetContainer(); + HashMapHolder::MapType::iterator itr = m.begin(); + for(; itr != m.end(); ++itr) + { + if( itr->second->GetSession()->GetSecurity() && (itr->second->isGameMaster() || sWorld.getConfig(CONFIG_GM_IN_GM_LIST) ) && + itr->second->IsVisibleGloballyFor(m_session->GetPlayer()) ) + { + if(first) + { + SendSysMessage(LANG_GMS_ON_SRV); + first = false; + } + + SendSysMessage(itr->second->GetName()); + } + } + + if(first) + SendSysMessage(LANG_GMS_NOT_LOGGED); + + return true; +} + +bool ChatHandler::HandlePasswordCommand(const char* args) +{ + if(!*args) + return false; + + char *old_pass = strtok ((char*)args, " "); + char *new_pass = strtok (NULL, " "); + char *new_pass_c = strtok (NULL, " "); + + if( !old_pass || !new_pass || !new_pass_c ) + return false; + + std::string password_old = old_pass; + std::string password_new = new_pass; + std::string password_new_c = new_pass_c; + + if(!accmgr.CheckPassword(m_session->GetAccountId(), password_old) || password_new != password_new_c) + { + SendSysMessage(LANG_COMMAND_WRONGOLDPASSWORD); + SetSentErrorMessage(true); + return false; + } + + AccountOpResult result = accmgr.ChangePassword(m_session->GetAccountId(), password_new); + + switch(result) + { + case AOR_OK: + SendSysMessage(LANG_COMMAND_PASSWORD); + break; + default: + SendSysMessage(LANG_COMMAND_NOTCHANGEPASSWORD); + SetSentErrorMessage(true); + return false; + } + + return true; +} + +bool ChatHandler::HandleLockAccountCommand(const char* args) +{ + if (!*args) + { + SendSysMessage(LANG_USE_BOL); + return true; + } + + std::string argstr = (char*)args; + if (argstr == "on") + { + loginDatabase.PExecute( "UPDATE account SET locked = '1' WHERE id = '%d'",m_session->GetAccountId()); + PSendSysMessage(LANG_COMMAND_ACCLOCKLOCKED); + return true; + } + + if (argstr == "off") + { + loginDatabase.PExecute( "UPDATE account SET locked = '0' WHERE id = '%d'",m_session->GetAccountId()); + PSendSysMessage(LANG_COMMAND_ACCLOCKUNLOCKED); + return true; + } + + SendSysMessage(LANG_USE_BOL); + return true; +} diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp new file mode 100644 index 00000000000..21c1eec9f16 --- /dev/null +++ b/src/game/Level1.cpp @@ -0,0 +1,2351 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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) + { + SendSysMessage(LANG_USE_BOL); + SetSentErrorMessage(true); + return false; + } + + std::string argstr = (char*)args; + + if (argstr == "on") + { + m_session->GetPlayer()->SetGameMaster(true); + m_session->SendNotification("GM mode is 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("GM mode is OFF"); + #ifdef _DEBUG_VMAPS + VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); + vMapManager->processCommand("startlog"); + #endif + 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(GetMangosString(LANG_INVISIBLE_VISIBLE)); + return true; + } + + if (argstr == "off") + { + m_session->SendNotification(GetMangosString(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()] : "" ), + zone_id, (zoneEntry ? zoneEntry->area_name[m_session->GetSessionDbcLocale()] : "" ), + area_id, (areaEntry ? areaEntry->area_name[m_session->GetSessionDbcLocale()] : "" ), + 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()] : "" ), + zone_id, (zoneEntry ? zoneEntry->area_name[sWorld.GetDefaultDbcLocale()] : "" ), + area_id, (areaEntry ? areaEntry->area_name[sWorld.GetDefaultDbcLocale()] : "" ), + 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->Instanceable()) + { + 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->Instanceable()) + { + 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; +} + +//Teleport by game_tele entry +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(); + + char* cId = extractKeyFromLink((char*)args,"Htele"); // string or [name] Shift-click form |color|Htele:name|h[name]|h|r + if(!cId) + return false; + + std::string name = cId; + WorldDatabase.escape_string(name); + + QueryResult *result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM game_tele WHERE name = '%s'",name.c_str()); + if (!result) + { + SendSysMessage(LANG_COMMAND_TELE_NOTFOUND); + SetSentErrorMessage(true); + return false; + } + 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,x,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::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; + WorldDatabase.escape_string(namepart); + QueryResult *result = WorldDatabase.PQuery("SELECT name FROM game_tele WHERE name "_LIKE_" '""%%%s%%""'",namepart.c_str()); + if (!result) + { + SendSysMessage(LANG_COMMAND_TELE_NOREQUEST); + SetSentErrorMessage(true); + return false; + } + std::string reply; + for (uint64 i=0; i < result->GetRowCount(); i++) + { + Field *fields = result->Fetch(); + reply += " |cffffffff|Htele:"; + reply += fields[0].GetCppString(); + reply += "|h["; + reply += fields[0].GetCppString(); + reply += "]|h|r\n"; + result->NextRow(); + } + delete result; + + if(reply.empty()) + SendSysMessage(LANG_COMMAND_TELE_NOLOCATION); + else + { + reply = GetMangosString(LANG_COMMAND_TELE_LOCATION) + reply; + SendSysMessage(reply.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; + + char* pName = strtok((char*)args, " "); + char* msgSubject = strtok(NULL, " "); + char* 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; + + if(!normalizePlayerName(name)) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + uint64 receiver_guid = objmgr.GetPlayerGUIDByName(name); + + if(!receiver_guid) + 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); + + WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, NULL, 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; + + char* tail = strtok(NULL, ""); + if(!tail) + return false; + + char* cId = extractKeyFromLink((char*)tail,"Htele"); // string or [name] Shift-click form |color|Htele:name|h[name]|h|r + if(!cId) + return false; + + std::string location = cId; + + std::string name = pName; + + if(!normalizePlayerName(name)) + { + SendSysMessage(LANG_PLAYER_NOT_FOUND); + SetSentErrorMessage(true); + return false; + } + + WorldDatabase.escape_string(location); + QueryResult *result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM game_tele WHERE name = '%s'",location.c_str()); + if (!result) + { + SendSysMessage(LANG_COMMAND_TELE_NOTFOUND); + SetSentErrorMessage(true); + return false; + } + + 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,x,ort)) + { + PSendSysMessage(LANG_INVALID_TARGET_COORD,x,y,mapid); + 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(),"", location.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(mapid,x,y,z,chr->GetOrientation()); + } + else if (uint64 guid = objmgr.GetPlayerGUIDByName(name.c_str())) + { + PSendSysMessage(LANG_TELEPORTING_TO, name.c_str(), GetMangosString(LANG_OFFLINE), location.c_str()); + Player::SavePositionInDB(mapid,x,y,z,ort,MapManager::Instance().GetZoneId(mapid,x,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; + } + + char* cId = extractKeyFromLink((char*)args,"Htele"); // string or [name] Shift-click form |color|Htele:name|h[name]|h|r + if(!cId) + return false; + + std::string location = cId; + + WorldDatabase.escape_string(location); + QueryResult *result = WorldDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map FROM game_tele WHERE name = '%s'",location.c_str()); + if (!result) + { + SendSysMessage(LANG_COMMAND_TELE_NOTFOUND); + SetSentErrorMessage(true); + return false; + } + 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; + } + + 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(),"", location.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(mapid, x, y, z, ort); + } + + 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 new file mode 100644 index 00000000000..63ad2a7a0b9 --- /dev/null +++ b/src/game/Level2.cpp @@ -0,0 +1,3948 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 "WaypointManager.h" +#include "Util.h" +#include +#include +#include +#include + +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_" '""%%%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 --> 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(data)->posX = x; + const_cast(data)->posY = y; + const_cast(data)->posZ = z; + const_cast(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; + + 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); + + 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; + + ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId); + if(!pProto) + { + PSendSysMessage(LANG_ITEM_NOT_FOUND, itemId); + SetSentErrorMessage(true); + return false; + } + + if(extendedcost && !sItemExtendedCostStore.LookupEntry(extendedcost)) + { + PSendSysMessage(LANG_BAD_VALUE, extendedcost); + SetSentErrorMessage(true); + return false; + } + + // load vendor items if not yet + vendor->LoadGoods(); + + if(vendor->FindItem(itemId)) + { + PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST,itemId); + SetSentErrorMessage(true); + return false; + } + + if (vendor->GetItemCount() >= MAX_VENDOR_ITEMS) + { + SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS); + SetSentErrorMessage(true); + return false; + } + + // add to DB and to current ingame vendor + WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')",vendor->GetEntry(), itemId, maxcount,incrtime,extendedcost); + vendor->AddItem(itemId,maxcount,incrtime,extendedcost); + 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); + + ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId); + if(!pProto) + { + PSendSysMessage(LANG_ITEM_NOT_FOUND, itemId); + SetSentErrorMessage(true); + return false; + } + + // load vendor items if not yet + vendor->LoadGoods(); + + if (!vendor->RemoveItem(itemId)) + { + PSendSysMessage(LANG_ITEM_NOT_IN_LIST,itemId); + SetSentErrorMessage(true); + return false; + } + + WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",vendor->GetEntry(), 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.
+ *
+ * Valid movement types are: + *
    + *
  • stay - NPC wont move
  • + *
  • random - NPC will move randomly according to the spawndist
  • + *
  • way - NPC will move with given waypoints set
  • + *
+ * 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(cinfo)->faction_A = factionId; + const_cast(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 + 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 + * User has selected a visual waypoint before. + * is added to this waypoint. Everytime the + * NPC comes to this waypoint, the emote is called. + * + * emote + * User has not selected visual waypoint before. + * For the waypoint for the NPC with + * an emote is added. + * Everytime the NPC comes to this waypoint, the emote is called. + * + * + * info -> 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: + + // 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 + + 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 -> 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 -> 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 -> 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() : ""), 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: + + // 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 + 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; +} diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp new file mode 100644 index 00000000000..93f39229b7d --- /dev/null +++ b/src/game/Level3.cpp @@ -0,0 +1,5424 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 "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(""); + //HandleReloadAreaTriggerTavernCommand(""); -- reloaded in HandleReloadAreaTriggerTavernCommand + //HandleReloadQuestAreaTriggersCommand(""); -- reloaded in HandleReloadAllQuestCommand + HandleReloadSkillFishingBaseLevelCommand(""); + + HandleReloadAllLootCommand(""); + HandleReloadAllQuestCommand(""); + HandleReloadAllSpellCommand(""); + HandleReloadAllItemCommand(""); + + HandleReloadCommandCommand(""); + HandleReloadReservedNameCommand(""); + HandleReloadMangosStringCommand(""); + return true; +} + +bool ChatHandler::HandleReloadAllAreaCommand(const char*) +{ + HandleReloadAreaTriggerTeleportCommand(""); + //HandleReloadAreaTriggerTavernCommand(""); -- reloaded in HandleReloadAreaTriggerTavernCommand + 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::HandleReloadAllLootCommand(const char*) +{ + sLog.outString( "Re-Loading Loot Tables..." ); + LoadLootTables(); + SendGlobalSysMessage("DB tables `*_loot_template` 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::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::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;spellHasSpell(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(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(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(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; + QueryResult *result; + Player *player=m_session->GetPlayer(); + if (!player) return false; + + std::string name = args; + WorldDatabase.escape_string(name); + result = WorldDatabase.PQuery("SELECT id FROM game_tele WHERE name = '%s'",name.c_str()); + if (result) + { + SendSysMessage(LANG_COMMAND_TP_ALREADYEXIST); + delete result; + SetSentErrorMessage(true); + return false; + } + + float x = player->GetPositionX(); + float y = player->GetPositionY(); + float z = player->GetPositionZ(); + float ort = player->GetOrientation(); + int mapid = player->GetMapId(); + + if(WorldDatabase.PExecuteLog("INSERT INTO game_tele (position_x,position_y,position_z,orientation,map,name) VALUES (%f,%f,%f,%f,%d,'%s')",x,y,z,ort,mapid,name.c_str())) + { + 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; + WorldDatabase.escape_string(name); + + QueryResult *result=WorldDatabase.PQuery("SELECT id FROM game_tele WHERE name = '%s'",name.c_str()); + if (!result) + { + SendSysMessage(LANG_COMMAND_TELE_NOTFOUND); + SetSentErrorMessage(true); + return false; + } + delete result; + + if(WorldDatabase.PExecuteLog("DELETE FROM game_tele WHERE name = '%s'",name.c_str())) + { + SendSysMessage(LANG_COMMAND_TP_DELETED); + } + else + { + SendSysMessage(LANG_COMMAND_TP_DELETEERR); + SetSentErrorMessage(true); + return false; + } + 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::MapType const& plist = ObjectAccessor::Instance().GetPlayers(); + for(HashMapHolder::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_" '""%%%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_" '""%%%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_" '""%%%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 worker(u_do); + + TypeContainerVisitor, GridTypeMapContainer > obj_worker(worker); + CellLock 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 const* mgen = static_cast 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 const* mgen = static_cast 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 = ""; 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; +} diff --git a/src/game/LootHandler.cpp b/src/game/LootHandler.cpp new file mode 100644 index 00000000000..05c0ac8a997 --- /dev/null +++ b/src/game/LootHandler.cpp @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "WorldPacket.h" +#include "Log.h" +#include "Corpse.h" +#include "GameObject.h" +#include "Player.h" +#include "ObjectAccessor.h" +#include "WorldSession.h" +#include "LootMgr.h" +#include "Object.h" +#include "Group.h" +#include "World.h" +#include "Util.h" + +void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,1); + + sLog.outDebug("WORLD: CMSG_AUTOSTORE_LOOT_ITEM"); + Player *player = GetPlayer(); + uint64 lguid = player->GetLootGUID(); + Loot *loot; + uint8 lootSlot; + + recv_data >> lootSlot; + + if (IS_GAMEOBJECT_GUID(lguid)) + { + GameObject *go = + ObjectAccessor::GetGameObject(*player, lguid); + + // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO + if (!go || (go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) + { + player->SendLootRelease(lguid); + return; + } + + loot = &go->loot; + } + else if (IS_ITEM_GUID(lguid)) + { + Item *pItem = player->GetItemByGuid( lguid ); + + if (!pItem) + { + player->SendLootRelease(lguid); + return; + } + + loot = &pItem->loot; + } + else + { + Creature* pCreature = + ObjectAccessor::GetCreature(*player, lguid); + + bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed); + + if( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) + { + player->SendLootRelease(lguid); + return; + } + + loot = &pCreature->loot; + } + + QuestItem *qitem = NULL; + QuestItem *ffaitem = NULL; + QuestItem *conditem = NULL; + + LootItem *item = loot->LootItemInSlot(lootSlot,player,&qitem,&ffaitem,&conditem); + + if(!item) + { + player->SendEquipError( EQUIP_ERR_ALREADY_LOOTED, NULL, NULL ); + return; + } + + // questitems use the blocked field for other purposes + if (!qitem && item->is_blocked) + { + player->SendLootRelease(lguid); + return; + } + + 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); + + if (qitem) + { + qitem->is_looted = true; + //freeforall is 1 if everyone's supposed to get the quest item. + if (item->freeforall || loot->GetPlayerQuestItems().size() == 1) + player->SendNotifyLootItemRemoved(lootSlot); + else + loot->NotifyQuestItemRemoved(qitem->index); + } + else + { + if (ffaitem) + { + //freeforall case, notify only one player of the removal + ffaitem->is_looted=true; + player->SendNotifyLootItemRemoved(lootSlot); + } + else + { + //not freeforall, notify everyone + if(conditem) + conditem->is_looted=true; + loot->NotifyItemRemoved(lootSlot); + } + } + + //if only one person is supposed to loot the item, then set it to looted + if (!item->freeforall) + item->is_looted = true; + + --loot->unlootedCount; + + player->SendNewItem(newitem, uint32(item->count), false, false, true); + } + else + player->SendEquipError( msg, NULL, NULL ); +} + +void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug("WORLD: CMSG_LOOT_MONEY"); + + Player *player = GetPlayer(); + uint64 guid = player->GetLootGUID(); + if(!guid) + return; + + Loot *pLoot = NULL; + + switch(GUID_HIPART(guid)) + { + case HIGHGUID_GAMEOBJECT: + { + GameObject *pGameObject = ObjectAccessor::GetGameObject(*GetPlayer(), guid); + + // not check distance for GO in case owned GO (fishing bobber case, for example) + if( pGameObject && (pGameObject->GetOwnerGUID()==_player->GetGUID() || pGameObject->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) ) + pLoot = &pGameObject->loot; + + break; + } + case HIGHGUID_CORPSE: // remove insignia ONLY in BG + { + Corpse *bones = ObjectAccessor::GetCorpse(*GetPlayer(), guid); + + if (bones && bones->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) + pLoot = &bones->loot; + + break; + } + case HIGHGUID_ITEM: + { + if(Item *item = GetPlayer()->GetItemByGuid(guid)) + pLoot = &item->loot; + break; + } + case HIGHGUID_UNIT: + { + Creature* pCreature = ObjectAccessor::GetCreature(*GetPlayer(), guid); + + bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed); + + if ( ok_loot && pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) + pLoot = &pCreature->loot ; + + break; + } + default: + return; // unlootable type + } + + if( pLoot ) + { + if (!IS_ITEM_GUID(guid) && player->GetGroup()) //item can be looted only single player + { + Group *group = player->GetGroup(); + + std::vector playersNear; + for(GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* playerGroup = itr->getSource(); + if(!playerGroup) + continue; + if (player->GetDistance2d(playerGroup) < sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE)) + playersNear.push_back(playerGroup); + } + + uint32 money_per_player = uint32((pLoot->gold)/(playersNear.size())); + + for (std::vector::iterator i = playersNear.begin(); i != playersNear.end(); ++i) + { + (*i)->ModifyMoney( money_per_player ); + //Offset surely incorrect, but works + WorldPacket data( SMSG_LOOT_MONEY_NOTIFY, 4 ); + data << uint32(money_per_player); + (*i)->GetSession()->SendPacket( &data ); + } + } + else + player->ModifyMoney( pLoot->gold ); + pLoot->gold = 0; + pLoot->NotifyMoneyRemoved(); + } +} + +void WorldSession::HandleLootOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + sLog.outDebug("WORLD: CMSG_LOOT"); + + uint64 guid; + recv_data >> guid; + + GetPlayer()->SendLoot(guid, LOOT_CORPSE); +} + +void WorldSession::HandleLootReleaseOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + sLog.outDebug("WORLD: CMSG_LOOT_RELEASE"); + + // cheaters can modify lguid to prevent correct apply loot release code and re-loot + // use internal stored guid + //uint64 lguid; + //recv_data >> lguid; + + if(uint64 lguid = GetPlayer()->GetLootGUID()) + DoLootRelease(lguid); +} + +void WorldSession::DoLootRelease( uint64 lguid ) +{ + Player *player = GetPlayer(); + Loot *loot; + + player->SetLootGUID(0); + player->SendLootRelease(lguid); + + player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_LOOTING); + + if (IS_GAMEOBJECT_GUID(lguid)) + { + GameObject *go = + ObjectAccessor::GetGameObject(*player, lguid); + + // not check distance for GO in case owned GO (fishing bobber case, for example) or Fishing hole GO + if (!go || (go->GetOwnerGUID() != _player->GetGUID() && go->GetGoType() != GAMEOBJECT_TYPE_FISHINGHOLE) && !go->IsWithinDistInMap(_player,INTERACTION_DISTANCE)) + return; + + loot = &go->loot; + + if (go->GetGoType() == GAMEOBJECT_TYPE_DOOR) + { + // locked doors are opened with spelleffect openlock, prevent remove its as looted + go->UseDoorOrButton(); + } + else if (loot->isLooted() || go->GetGoType() == GAMEOBJECT_TYPE_FISHINGNODE) + { + // GO is mineral vein? so it is not removed after its looted + if(go->GetGoType() == GAMEOBJECT_TYPE_CHEST) + { + uint32 go_min = go->GetGOInfo()->chest.minSuccessOpens; + uint32 go_max = go->GetGOInfo()->chest.maxSuccessOpens; + + // only vein pass this check + if(go_min != 0 && go_max > go_min) + { + float amount_rate = sWorld.getRate(RATE_MINING_AMOUNT); + float min_amount = go_min*amount_rate; + float max_amount = go_max*amount_rate; + + go->AddUse(); + float uses = float(go->GetUseCount()); + + if(uses < max_amount) + { + if(uses >= min_amount) + { + float chance_rate = sWorld.getRate(RATE_MINING_NEXT); + + int32 ReqValue = 175; + LockEntry const *lockInfo = sLockStore.LookupEntry(go->GetGOInfo()->chest.lockId); + if(lockInfo) + ReqValue = lockInfo->requiredminingskill; + float skill = float(player->GetSkillValue(SKILL_MINING))/(ReqValue+25); + double chance = pow(0.8*chance_rate,4*(1/double(max_amount))*double(uses)); + if(roll_chance_f(100*chance+skill)) + { + go->SetLootState(GO_READY); + } + else // not have more uses + go->SetLootState(GO_JUST_DEACTIVATED); + } + else // 100% chance until min uses + go->SetLootState(GO_READY); + } + else // max uses already + go->SetLootState(GO_JUST_DEACTIVATED); + } + else // not vein + go->SetLootState(GO_JUST_DEACTIVATED); + } + else if (go->GetGoType() == GAMEOBJECT_TYPE_FISHINGHOLE) + { // The fishing hole used once more + go->AddUse(); // if the max usage is reached, will be despawned in next tick + if (go->GetUseCount()>=irand(go->GetGOInfo()->fishinghole.minSuccessOpens,go->GetGOInfo()->fishinghole.maxSuccessOpens)) + { + go->SetLootState(GO_JUST_DEACTIVATED); + } + else + go->SetLootState(GO_READY); + } + else // not chest (or vein/herb/etc) + go->SetLootState(GO_JUST_DEACTIVATED); + + loot->clear(); + } + else + // not fully looted object + go->SetLootState(GO_ACTIVATED); + } + else if (IS_CORPSE_GUID(lguid)) // ONLY remove insignia at BG + { + Corpse *corpse = ObjectAccessor::GetCorpse(*player, lguid); + if (!corpse || !corpse->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) + return; + + loot = &corpse->loot; + + if (loot->isLooted()) + { + loot->clear(); + corpse->RemoveFlag(CORPSE_FIELD_DYNAMIC_FLAGS, CORPSE_DYNFLAG_LOOTABLE); + } + } + else if (IS_ITEM_GUID(lguid)) + { + Item *pItem = player->GetItemByGuid(lguid ); + if(!pItem) + return; + if( (pItem->GetProto()->BagFamily & BAG_FAMILY_MASK_MINING_SUPP) && + pItem->GetProto()->Class == ITEM_CLASS_TRADE_GOODS && + pItem->GetCount() >= 5) + { + pItem->m_lootGenerated = false; + pItem->loot.clear(); + + uint32 count = 5; + player->DestroyItemCount(pItem, count, true); + } + else + // FIXME: item don't must be deleted in case not fully looted state. But this pre-request implement loot saving in DB at item save. Or checting possible. + player->DestroyItem( pItem->GetBagSlot(),pItem->GetSlot(), true); + return; // item can be looted only single player + } + else + { + Creature* pCreature = ObjectAccessor::GetCreature(*player, lguid); + + bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass()==CLASS_ROGUE && pCreature->lootForPickPocketed); + if ( !ok_loot || !pCreature->IsWithinDistInMap(_player,INTERACTION_DISTANCE) ) + return; + + loot = &pCreature->loot; + + // update next looter + if(Player *recipient = pCreature->GetLootRecipient()) + if(Group* group = recipient->GetGroup()) + if (group->GetLooterGuid() == player->GetGUID()) + group->UpdateLooterGuid(pCreature); + + if (loot->isLooted()) + { + // skip pickpocketing loot for speed, skinning timer redunction is no-op in fact + if(!pCreature->isAlive()) + pCreature->AllLootRemovedFromCorpse(); + + pCreature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); + loot->clear(); + } + } + + //Player is not looking at loot list, he doesn't need to see updates on the loot list + loot->RemoveLooter(player->GetGUID()); +} + +void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+1+8); + + uint8 slotid; + uint64 lootguid, target_playerguid; + + recv_data >> lootguid >> slotid >> target_playerguid; + + if(!_player->GetGroup() || _player->GetGroup()->GetLooterGuid() != _player->GetGUID()) + { + _player->SendLootRelease(GetPlayer()->GetLootGUID()); + return; + } + + Player *target = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(target_playerguid, 0, HIGHGUID_PLAYER)); + if(!target) + return; + + sLog.outDebug("WorldSession::HandleLootMasterGiveOpcode (CMSG_LOOT_MASTER_GIVE, 0x02A3) Target = [%s].", target->GetName()); + + if(_player->GetLootGUID() != lootguid) + return; + + Loot *pLoot = NULL; + + if(IS_CREATURE_GUID(GetPlayer()->GetLootGUID())) + { + Creature *pCreature = ObjectAccessor::GetCreature(*GetPlayer(), lootguid); + if(!pCreature) + return; + + pLoot = &pCreature->loot; + } + else if(IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID())) + { + GameObject *pGO = ObjectAccessor::GetGameObject(*GetPlayer(), lootguid); + if(!pGO) + return; + + pLoot = &pGO->loot; + } + + if(!pLoot) + return; + + if (slotid > pLoot->items.size()) + { + sLog.outDebug("AutoLootItem: Player %s might be using a hack! (slot %d, size %d)",GetPlayer()->GetName(), slotid, pLoot->items.size()); + return; + } + + LootItem& item = pLoot->items[slotid]; + + ItemPosCountVec dest; + uint8 msg = target->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item.itemid, item.count ); + if ( msg != EQUIP_ERR_OK ) + { + target->SendEquipError( msg, NULL, NULL ); + _player->SendEquipError( msg, NULL, NULL ); // send duplicate of error massage to master looter + return; + } + + // not move item from loot to target inventory + Item * newitem = target->StoreNewItem( dest, item.itemid, true, item.randomPropertyId ); + target->SendNewItem(newitem, uint32(item.count), false, false, true ); + + // mark as looted + item.count=0; + item.is_looted=true; + + + pLoot->NotifyItemRemoved(slotid); + --pLoot->unlootedCount; +} diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp new file mode 100644 index 00000000000..e7848ff4094 --- /dev/null +++ b/src/game/LootMgr.cpp @@ -0,0 +1,1239 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "LootMgr.h" +#include "Log.h" +#include "ObjectMgr.h" +#include "ProgressBar.h" +#include "World.h" +#include "Util.h" +#include "SharedDefines.h" + +static Rates const qualityToRate[MAX_ITEM_QUALITY] = { + RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR + RATE_DROP_ITEM_NORMAL, // ITEM_QUALITY_NORMAL + RATE_DROP_ITEM_UNCOMMON, // ITEM_QUALITY_UNCOMMON + RATE_DROP_ITEM_RARE, // ITEM_QUALITY_RARE + RATE_DROP_ITEM_EPIC, // ITEM_QUALITY_EPIC + RATE_DROP_ITEM_LEGENDARY, // ITEM_QUALITY_LEGENDARY + RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT +}; + +LootStore LootTemplates_Creature( "creature_loot_template", "creature entry"); +LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id"); +LootStore LootTemplates_Fishing( "fishing_loot_template", "area id"); +LootStore LootTemplates_Gameobject( "gameobject_loot_template", "gameobject entry"); +LootStore LootTemplates_Item( "item_loot_template", "item entry"); +LootStore LootTemplates_Pickpocketing("pickpocketing_loot_template","creature pickpocket lootid"); +LootStore LootTemplates_Prospecting( "prospecting_loot_template", "item entry"); +LootStore LootTemplates_QuestMail( "quest_mail_loot_template", "quest id"); +LootStore LootTemplates_Reference( "reference_loot_template", "reference id"); +LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id"); + + +class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed) +{ + public: + void AddEntry(LootStoreItem& item); // Adds an entry to the group (at loading stage) + bool HasQuestDrop() const; // True if group includes at least 1 quest drop entry + bool HasQuestDropForPlayer(Player const * player) const; + // The same for active quests of the player + void Process(Loot& loot) const; // Rolls an item from the group (if any) and adds the item to the loot + float RawTotalChance() const; // Overall chance for the group (without equal chanced items) + float TotalChance() const; // Overall chance for the group + + void Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const; + void CollectLootIds(LootIdSet& set) const; + void CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const; + private: + LootStoreItemList ExplicitlyChanced; // Entries with chances defined in DB + LootStoreItemList EqualChanced; // Zero chances - every entry takes the same chance + + LootStoreItem const * Roll() const; // Rolls an item from the group, returns NULL if all miss their chances +}; + +//Remove all data and free all memory +void LootStore::Clear() +{ + for (LootTemplateMap::const_iterator itr=m_LootTemplates.begin(); itr != m_LootTemplates.end(); ++itr) + delete itr->second; + m_LootTemplates.clear(); +} + +// Checks validity of the loot store +// Actual checks are done within LootTemplate::Verify() which is called for every template +void LootStore::Verify() const +{ + for (LootTemplateMap::const_iterator i = m_LootTemplates.begin(); i != m_LootTemplates.end(); ++i ) + i->second->Verify(*this, i->first); +} + +// Loads a *_loot_template DB table into loot store +// All checks of the loaded template are called from here, no error reports at loot generation required +void LootStore::LoadLootTable() +{ + LootTemplateMap::iterator tab; + uint32 count = 0; + + // Clearing store (for reloading case) + Clear(); + + sLog.outString( "%s :", GetName()); + + // 0 1 2 3 4 5 6 7 8 + QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, lootcondition, condition_value1, condition_value2 FROM %s",GetName()); + + if (result) + { + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 entry = fields[0].GetUInt32(); + uint32 item = fields[1].GetUInt32(); + float chanceOrQuestChance = fields[2].GetFloat(); + uint8 group = fields[3].GetUInt8(); + int32 mincountOrRef = fields[4].GetInt32(); + uint8 maxcount = fields[5].GetUInt8(); + ConditionType condition = (ConditionType)fields[6].GetUInt8(); + uint32 cond_value1 = fields[7].GetUInt32(); + uint32 cond_value2 = fields[8].GetUInt32(); + + if(!PlayerCondition::IsValid(condition,cond_value1, cond_value2)) + { + sLog.outErrorDb("... in table '%s' entry %u item %u", GetName(), entry, item); + continue; // error already printed to log/console. + } + + // (condition + cond_value1/2) are converted into single conditionId + uint16 conditionId = objmgr.GetConditionId(condition, cond_value1, cond_value2); + + LootStoreItem storeitem = LootStoreItem(item, chanceOrQuestChance, group, conditionId, mincountOrRef, maxcount); + + if (!storeitem.IsValid(*this,entry)) // Validity checks + continue; + + // Looking for the template of the entry + // often entries are put together + if (m_LootTemplates.empty() || tab->first != entry) + { + // Searching the template (in case template Id changed) + tab = m_LootTemplates.find(entry); + if ( tab == m_LootTemplates.end() ) + { + std::pair< LootTemplateMap::iterator, bool > pr = m_LootTemplates.insert(LootTemplateMap::value_type(entry, new LootTemplate)); + tab = pr.first; + } + } + // else is empty - template Id and iter are the same + // finally iter refers to already existed or just created + + // Adds current row to the template + tab->second->AddEntry(storeitem); + ++count; + + } while (result->NextRow()); + + delete result; + + Verify(); // Checks validity of the loot store + + sLog.outString(); + sLog.outString( ">> Loaded %u loot definitions (%d templates)", count, m_LootTemplates.size()); + } + else + { + sLog.outString(); + sLog.outErrorDb( ">> Loaded 0 loot definitions. DB table `%s` is empty.",GetName() ); + } +} + +bool LootStore::HaveQuestLootFor(uint32 loot_id) const +{ + LootTemplateMap::const_iterator itr = m_LootTemplates.find(loot_id); + if(itr == m_LootTemplates.end()) + return false; + + // scan loot for quest items + return itr->second->HasQuestDrop(m_LootTemplates); +} + +bool LootStore::HaveQuestLootForPlayer(uint32 loot_id,Player* player) const +{ + LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id); + if (tab != m_LootTemplates.end()) + if (tab->second->HasQuestDropForPlayer(m_LootTemplates, player)) + return true; + + return false; +} + +LootTemplate const* LootStore::GetLootFor(uint32 loot_id) const +{ + LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id); + + if (tab == m_LootTemplates.end()) + return NULL; + + return tab->second; +} + +void LootStore::LoadAndCollectLootIds(LootIdSet& ids_set) +{ + LoadLootTable(); + + for(LootTemplateMap::const_iterator tab = m_LootTemplates.begin(); tab != m_LootTemplates.end(); ++tab) + ids_set.insert(tab->first); +} + +void LootStore::CheckLootRefs(LootIdSet* ref_set) const +{ + for(LootTemplateMap::const_iterator ltItr = m_LootTemplates.begin(); ltItr != m_LootTemplates.end(); ++ltItr) + ltItr->second->CheckLootRefs(m_LootTemplates,ref_set); +} + +void LootStore::ReportUnusedIds(LootIdSet const& ids_set) const +{ + // all still listed ids isn't referenced + for(LootIdSet::const_iterator itr = ids_set.begin(); itr != ids_set.end(); ++itr) + sLog.outErrorDb("Table '%s' entry %d isn't %s and not referenced from loot, and then useless.", GetName(), *itr,GetEntryName()); +} + +void LootStore::ReportNotExistedId(uint32 id) const +{ + sLog.outErrorDb("Table '%s' entry %d (%s) not exist but used as loot id in DB.", GetName(), id,GetEntryName()); +} + +// +// --------- LootStoreItem --------- +// + +// Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation) +// RATE_DROP_ITEMS is no longer used for all types of entries +bool LootStoreItem::Roll() const +{ + if(chance>=100.f) + return true; + + if(mincountOrRef < 0) // reference case + return roll_chance_f(chance*sWorld.getRate(RATE_DROP_ITEM_REFERENCED)); + + ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid); + + float qualityModifier = pProto ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f; + + return roll_chance_f(chance*qualityModifier); +} + +// Checks correctness of values +bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const +{ + if (mincountOrRef == 0) + { + sLog.outErrorDb("Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped", store.GetName(), entry, itemid, mincountOrRef); + return false; + } + + if( mincountOrRef > 0 ) // item (quest or non-quest) entry, maybe grouped + { + ItemPrototype const *proto = objmgr.GetItemPrototype(itemid); + if(!proto) + { + sLog.outErrorDb("Table '%s' entry %d item %d: item entry not listed in `item_template` - skipped", store.GetName(), entry, itemid); + return false; + } + + if( chance == 0 && group == 0) // Zero chance is allowed for grouped entries only + { + sLog.outErrorDb("Table '%s' entry %d item %d: equal-chanced grouped entry, but group not defined - skipped", store.GetName(), entry, itemid); + return false; + } + + if( chance != 0 && chance < 0.000001f ) // loot with low chance + { + sLog.outErrorDb("Table '%s' entry %d item %d: low chance (%d) - skipped", store.GetName(), entry, itemid, chance); + return false; + } + } + else // mincountOrRef < 0 + { + if (needs_quest) + sLog.outErrorDb("Table '%s' entry %d item %d: quest chance will be treated as non-quest chance", store.GetName(), entry, itemid); + else if( chance == 0 ) // no chance for the reference + { + sLog.outErrorDb("Table '%s' entry %d item %d: zero chance is specified for a reference, skipped", store.GetName(), entry, itemid); + return false; + } + } + return true; // Referenced template existence is checked at whole store level +} + +// +// --------- LootItem --------- +// + +// Constructor, copies most fields from LootStoreItem and generates random count +LootItem::LootItem(LootStoreItem const& li) +{ + itemid = li.itemid; + conditionId = li.conditionId; + + ItemPrototype const* proto = objmgr.GetItemPrototype(itemid); + freeforall = proto && (proto->Flags & ITEM_FLAGS_PARTY_LOOT); + + needs_quest = li.needs_quest; + + count = urand(li.mincountOrRef, li.maxcount); // constructor called for mincountOrRef > 0 only + randomSuffix = GenerateEnchSuffixFactor(itemid); + randomPropertyId = Item::GenerateItemRandomPropertyId(itemid); + is_looted = 0; + is_blocked = 0; + is_underthreshold = 0; + is_counted = 0; +} + +// Basic checks for player/item compatibility - if false no chance to see the item in the loot +bool LootItem::AllowedForPlayer(Player const * player) const +{ + // DB conditions check + if ( !objmgr.IsPlayerMeetToCondition(player,conditionId) ) + return false; + + if ( needs_quest ) + { + // Checking quests for quest-only drop (check only quests requirements in this case) + if( !player->HasQuestForItem(itemid) ) + return false; + } + else + { + // Not quest only drop (check quest starting items for already accepted non-repeatable quests) + ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid); + if (pProto && pProto->StartQuest && player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE && !player->HasQuestForItem(itemid)) + return false; + } + + return true; +} + +// +// --------- Loot --------- +// + +// Inserts the item into the loot (called by LootTemplate processors) +void Loot::AddItem(LootStoreItem const & item) +{ + if (item.needs_quest) // Quest drop + { + if (quest_items.size() < MAX_NR_QUEST_ITEMS) + quest_items.push_back(LootItem(item)); + } + else if (items.size() < MAX_NR_LOOT_ITEMS) // Non-quest drop + { + items.push_back(LootItem(item)); + + // non-conditional one-player only items are counted here, + // free for all items are counted in FillFFALoot(), + // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot() + if( !item.conditionId ) + { + ItemPrototype const* proto = objmgr.GetItemPrototype(item.itemid); + if( !proto || (proto->Flags & ITEM_FLAGS_PARTY_LOOT)==0 ) + ++unlootedCount; + } + } +} + +// Calls processor of corresponding LootTemplate (which handles everything including references) +void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner) +{ + LootTemplate const* tab = store.GetLootFor(loot_id); + + if (!tab) + { + sLog.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.",store.GetName(),loot_id); + return; + } + + items.reserve(MAX_NR_LOOT_ITEMS); + quest_items.reserve(MAX_NR_QUEST_ITEMS); + + tab->Process(*this, store); // Processing is done there, callback via Loot::AddItem() + + // Setting access rights fow group-looting case + if(!loot_owner) + return; + Group * pGroup=loot_owner->GetGroup(); + if(!pGroup) + return; + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + //fill the quest item map for every player in the recipient's group + Player* pl = itr->getSource(); + if(!pl) + continue; + uint32 plguid = pl->GetGUIDLow(); + QuestItemMap::iterator qmapitr = PlayerQuestItems.find(plguid); + if (qmapitr == PlayerQuestItems.end()) + { + FillQuestLoot(pl); + } + qmapitr = PlayerFFAItems.find(plguid); + if (qmapitr == PlayerFFAItems.end()) + { + FillFFALoot(pl); + } + qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid); + if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end()) + { + FillNonQuestNonFFAConditionalLoot(pl); + } + } +} + +QuestItemList* Loot::FillFFALoot(Player* player) +{ + QuestItemList *ql = new QuestItemList(); + + for(uint8 i = 0; i < items.size(); i++) + { + LootItem &item = items[i]; + if(!item.is_looted && item.freeforall && item.AllowedForPlayer(player) ) + { + ql->push_back(QuestItem(i)); + ++unlootedCount; + } + } + if (ql->empty()) + { + delete ql; + return NULL; + } + + PlayerFFAItems[player->GetGUIDLow()] = ql; + return ql; +} + +QuestItemList* Loot::FillQuestLoot(Player* player) +{ + if (items.size() == MAX_NR_LOOT_ITEMS) return NULL; + QuestItemList *ql = new QuestItemList(); + + for(uint8 i = 0; i < quest_items.size(); i++) + { + LootItem &item = quest_items[i]; + if(!item.is_looted && item.AllowedForPlayer(player) ) + { + ql->push_back(QuestItem(i)); + + // questitems get blocked when they first apper in a + // player's quest vector + // + // increase once if one looter only, looter-times if free for all + if (item.freeforall || !item.is_blocked) + ++unlootedCount; + + item.is_blocked = true; + + if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS) + break; + } + } + if (ql->empty()) + { + delete ql; + return NULL; + } + + PlayerQuestItems[player->GetGUIDLow()] = ql; + return ql; +} + +QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player) +{ + QuestItemList *ql = new QuestItemList(); + + for(uint8 i = 0; i < items.size(); ++i) + { + LootItem &item = items[i]; + if(!item.is_looted && !item.freeforall && item.conditionId && item.AllowedForPlayer(player)) + { + ql->push_back(QuestItem(i)); + if(!item.is_counted) + { + ++unlootedCount; + item.is_counted=true; + } + } + } + if (ql->empty()) + { + delete ql; + return NULL; + } + + PlayerNonQuestNonFFAConditionalItems[player->GetGUIDLow()] = ql; + return ql; +} + +//=================================================== + +void Loot::NotifyItemRemoved(uint8 lootIndex) +{ + // notify all players that are looting this that the item was removed + // convert the index to the slot the player sees + std::set::iterator i_next; + for(std::set::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next) + { + i_next = i; + ++i_next; + if(Player* pl = ObjectAccessor::FindPlayer(*i)) + pl->SendNotifyLootItemRemoved(lootIndex); + else + PlayersLooting.erase(i); + } +} + +void Loot::NotifyMoneyRemoved() +{ + // notify all players that are looting this that the money was removed + std::set::iterator i_next; + for(std::set::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next) + { + i_next = i; + ++i_next; + if(Player* pl = ObjectAccessor::FindPlayer(*i)) + pl->SendNotifyLootMoneyRemoved(); + else + PlayersLooting.erase(i); + } +} + +void Loot::NotifyQuestItemRemoved(uint8 questIndex) +{ + // when a free for all questitem is looted + // all players will get notified of it being removed + // (other questitems can be looted by each group member) + // bit inefficient but isnt called often + + std::set::iterator i_next; + for(std::set::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next) + { + i_next = i; + ++i_next; + if(Player* pl = ObjectAccessor::FindPlayer(*i)) + { + QuestItemMap::iterator pq = PlayerQuestItems.find(pl->GetGUIDLow()); + if (pq != PlayerQuestItems.end() && pq->second) + { + // find where/if the player has the given item in it's vector + QuestItemList& pql = *pq->second; + + uint8 j; + for (j = 0; j < pql.size(); ++j) + if (pql[j].index == questIndex) + break; + + if (j < pql.size()) + pl->SendNotifyLootItemRemoved(items.size()+j); + } + } + else + PlayersLooting.erase(i); + } +} + +void Loot::generateMoneyLoot( uint32 minAmount, uint32 maxAmount ) +{ + if (maxAmount > 0) + { + if (maxAmount <= minAmount) + gold = uint32(maxAmount * sWorld.getRate(RATE_DROP_MONEY)); + else if ((maxAmount - minAmount) < 32700) + gold = uint32(urand(minAmount, maxAmount) * sWorld.getRate(RATE_DROP_MONEY)); + else + gold = uint32(urand(minAmount >> 8, maxAmount >> 8) * sWorld.getRate(RATE_DROP_MONEY)) << 8; + } +} + +LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qitem, QuestItem **ffaitem, QuestItem **conditem) +{ + LootItem* item = NULL; + bool is_looted = true; + if (lootSlot >= items.size()) + { + uint32 questSlot = lootSlot - items.size(); + QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUIDLow()); + if (itr != PlayerQuestItems.end() && questSlot < itr->second->size()) + { + QuestItem *qitem2 = &itr->second->at(questSlot); + if(qitem) + *qitem = qitem2; + item = &quest_items[qitem2->index]; + is_looted = qitem2->is_looted; + } + } + else + { + item = &items[lootSlot]; + is_looted = item->is_looted; + if(item->freeforall) + { + QuestItemMap::const_iterator itr = PlayerFFAItems.find(player->GetGUIDLow()); + if (itr != PlayerFFAItems.end()) + { + for(QuestItemList::iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter) + if(iter->index==lootSlot) + { + QuestItem *ffaitem2 = (QuestItem*)&(*iter); + if(ffaitem) + *ffaitem = ffaitem2; + is_looted = ffaitem2->is_looted; + break; + } + } + } + else if(item->conditionId) + { + QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUIDLow()); + if (itr != PlayerNonQuestNonFFAConditionalItems.end()) + { + for(QuestItemList::iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter) + { + if(iter->index==lootSlot) + { + QuestItem *conditem2 = (QuestItem*)&(*iter); + if(conditem) + *conditem = conditem2; + is_looted = conditem2->is_looted; + break; + } + } + } + } + } + + if(is_looted) + return NULL; + + return item; +} + +ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li) +{ + b << uint32(li.itemid); + b << uint32(li.count); // nr of items of this type + b << uint32(objmgr.GetItemPrototype(li.itemid)->DisplayInfoID); + b << uint32(li.randomSuffix); + b << uint32(li.randomPropertyId); + //b << uint8(0); // slot type - will send after this function call + return b; +} + +ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) +{ + Loot &l = lv.loot; + + uint8 itemsShown = 0; + + //gold + b << uint32(lv.permission!=NONE_PERMISSION ? l.gold : 0); + + size_t count_pos = b.wpos(); // pos of item count byte + b << uint8(0); // item count placeholder + + switch (lv.permission) + { + case GROUP_PERMISSION: + { + // You are not the items proprietary, so you can only see + // blocked rolled items and quest items, and !ffa items + for (uint8 i = 0; i < l.items.size(); ++i) + { + if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer)) + { + uint8 slot_type = (l.items[i].is_blocked || l.items[i].is_underthreshold) ? 0 : 1; + + b << uint8(i) << l.items[i]; //send the index and the item if it's not looted, and blocked or under threshold, free for all items will be sent later, only one-player loots here + b << uint8(slot_type); // 0 - get 1 - look only + ++itemsShown; + } + } + break; + } + case ALL_PERMISSION: + case MASTER_PERMISSION: + { + uint8 slot_type = (lv.permission==MASTER_PERMISSION) ? 2 : 0; + for (uint8 i = 0; i < l.items.size(); ++i) + { + if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer)) + { + b << uint8(i) << l.items[i]; //only send one-player loot items now, free for all will be sent later + b << uint8(slot_type); // 0 - get 2 - master selection + ++itemsShown; + } + } + break; + } + case NONE_PERMISSION: + default: + return b; // nothing output more + } + + if (lv.qlist) + { + for (QuestItemList::iterator qi = lv.qlist->begin() ; qi != lv.qlist->end(); ++qi) + { + LootItem &item = l.quest_items[qi->index]; + if (!qi->is_looted && !item.is_looted) + { + b << uint8(l.items.size() + (qi - lv.qlist->begin())); + b << item; + b << uint8(0); // allow loot + ++itemsShown; + } + } + } + + if (lv.ffalist) + { + for (QuestItemList::iterator fi = lv.ffalist->begin() ; fi != lv.ffalist->end(); ++fi) + { + LootItem &item = l.items[fi->index]; + if (!fi->is_looted && !item.is_looted) + { + b << uint8(fi->index) << item; + b << uint8(0); // allow loot + ++itemsShown; + } + } + } + + if (lv.conditionallist) + { + for (QuestItemList::iterator ci = lv.conditionallist->begin() ; ci != lv.conditionallist->end(); ++ci) + { + LootItem &item = l.items[ci->index]; + if (!ci->is_looted && !item.is_looted) + { + b << uint8(ci->index) << item; + b << uint8(0); // allow loot + ++itemsShown; + } + } + } + + //update number of items shown + b.put(count_pos,itemsShown); + + return b; +} + +// +// --------- LootTemplate::LootGroup --------- +// + +// Adds an entry to the group (at loading stage) +void LootTemplate::LootGroup::AddEntry(LootStoreItem& item) +{ + if (item.chance != 0) + ExplicitlyChanced.push_back(item); + else + EqualChanced.push_back(item); +} + +// Rolls an item from the group, returns NULL if all miss their chances +LootStoreItem const * LootTemplate::LootGroup::Roll() const +{ + if (!ExplicitlyChanced.empty()) // First explicitly chanced entries are checked + { + float Roll = rand_chance(); + + for (uint32 i=0; i=100.f) + return &ExplicitlyChanced[i]; + + ItemPrototype const *pProto = objmgr.GetItemPrototype(ExplicitlyChanced[i].itemid); + float qualityMultiplier = pProto ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f; + Roll -= ExplicitlyChanced[i].chance * qualityMultiplier; + if (Roll < 0) + return &ExplicitlyChanced[i]; + } + } + if (!EqualChanced.empty()) // If nothing selected yet - an item is taken from equal-chanced part + return &EqualChanced[irand(0, EqualChanced.size()-1)]; + + return NULL; // Empty drop from the group +} + +// True if group includes at least 1 quest drop entry +bool LootTemplate::LootGroup::HasQuestDrop() const +{ + for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i) + if (i->needs_quest) + return true; + for (LootStoreItemList::const_iterator i=EqualChanced.begin(); i != EqualChanced.end(); ++i) + if (i->needs_quest) + return true; + return false; +} + +// True if group includes at least 1 quest drop entry for active quests of the player +bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const * player) const +{ + for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i) + if (player->HasQuestForItem(i->itemid)) + return true; + for (LootStoreItemList::const_iterator i=EqualChanced.begin(); i != EqualChanced.end(); ++i) + if (player->HasQuestForItem(i->itemid)) + return true; + return false; +} + +// Rolls an item from the group (if any takes its chance) and adds the item to the loot +void LootTemplate::LootGroup::Process(Loot& loot) const +{ + LootStoreItem const * item = Roll(); + if (item != NULL) + loot.AddItem(*item); +} + +// Overall chance for the group without equal chanced items +float LootTemplate::LootGroup::RawTotalChance() const +{ + float result = 0; + + for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i) + if ( !i->needs_quest ) + result += i->chance; + + return result; +} + +// Overall chance for the group +float LootTemplate::LootGroup::TotalChance() const +{ + float result = RawTotalChance(); + + if (!EqualChanced.empty() && result < 100.0f) + return 100.0f; + + return result; +} + +void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const +{ + float chance = RawTotalChance(); + if (chance > 101.0f) // TODO: replace with 100% when DBs will be ready + { + sLog.outErrorDb("Table '%s' entry %u group %d has total chance > 100%% (%f)", lootstore.GetName(), id, group_id, chance); + } + + if(chance >= 100.0f && !EqualChanced.empty()) + { + sLog.outErrorDb("Table '%s' entry %u group %d has items with chance=0%% but group total chance >= 100%% (%f)", lootstore.GetName(), id, group_id, chance); + } +} + +void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const +{ + for (LootStoreItemList::const_iterator ieItr=ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr) + { + if(ieItr->mincountOrRef < 0) + { + if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef)) + LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef); + else if(ref_set) + ref_set->erase(-ieItr->mincountOrRef); + } + } + + for (LootStoreItemList::const_iterator ieItr=EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr) + { + if(ieItr->mincountOrRef < 0) + { + if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef)) + LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef); + else if(ref_set) + ref_set->erase(-ieItr->mincountOrRef); + } + } +} + +// +// --------- LootTemplate --------- +// + +// Adds an entry to the group (at loading stage) +void LootTemplate::AddEntry(LootStoreItem& item) +{ + if (item.group > 0 && item.mincountOrRef > 0) // Group + { + if (item.group >= Groups.size()) + Groups.resize(item.group); // Adds new group the the loot template if needed + Groups[item.group-1].AddEntry(item); // Adds new entry to the group + } + else // Non-grouped entries and references are stored together + Entries.push_back(item); +} + +// Rolls for every item in the template and adds the rolled items the the loot +void LootTemplate::Process(Loot& loot, LootStore const& store, uint8 groupId) const +{ + if (groupId) // Group reference uses own processing of the group + { + if (groupId > Groups.size()) + return; // Error message already printed at loading stage + + Groups[groupId-1].Process(loot); + return; + } + + // Rolling non-grouped items + for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; i++ ) + { + if ( !i->Roll() ) + continue; // Bad luck for the entry + + if (i->mincountOrRef < 0) // References processing + { + LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(-i->mincountOrRef); + + if(!Referenced) + continue; // Error message already printed at loading stage + + for (uint32 loop=0; loop < i->maxcount; ++loop )// Ref multiplicator + Referenced->Process(loot, store, i->group); // Ref processing + } + else // Plain entries (not a reference, not grouped) + loot.AddItem(*i); // Chance is already checked, just add + } + + // Now processing groups + for (LootGroups::const_iterator i = Groups.begin( ) ; i != Groups.end( ) ; i++ ) + i->Process(loot); +} + +// True if template includes at least 1 quest drop entry +bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) const +{ + if (groupId) // Group reference + { + if (groupId > Groups.size()) + return false; // Error message [should be] already printed at loading stage + return Groups[groupId-1].HasQuestDrop(); + } + + for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i ) + { + if (i->mincountOrRef < 0) // References + { + LootTemplateMap::const_iterator Referenced = store.find(-i->mincountOrRef); + if( Referenced ==store.end() ) + continue; // Error message [should be] already printed at loading stage + if (Referenced->second->HasQuestDrop(store, i->group) ) + return true; + } + else if ( i->needs_quest ) + return true; // quest drop found + } + + // Now processing groups + for (LootGroups::const_iterator i = Groups.begin() ; i != Groups.end() ; i++ ) + if (i->HasQuestDrop()) + return true; + + return false; +} + +// True if template includes at least 1 quest drop for an active quest of the player +bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player const* player, uint8 groupId) const +{ + if (groupId) // Group reference + { + if (groupId > Groups.size()) + return false; // Error message already printed at loading stage + return Groups[groupId-1].HasQuestDropForPlayer(player); + } + + // Checking non-grouped entries + for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; i++ ) + { + if (i->mincountOrRef < 0) // References processing + { + LootTemplateMap::const_iterator Referenced = store.find(-i->mincountOrRef); + if (Referenced == store.end() ) + continue; // Error message already printed at loading stage + if (Referenced->second->HasQuestDropForPlayer(store, player, i->group) ) + return true; + } + else if ( player->HasQuestForItem(i->itemid) ) + return true; // active quest drop found + } + + // Now checking groups + for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i ) + if (i->HasQuestDrop()) + return true; + + return false; +} + +// Checks integrity of the template +void LootTemplate::Verify(LootStore const& lootstore, uint32 id) const +{ + // Checking group chances + for (uint32 i=0; i < Groups.size(); ++i) + Groups[i].Verify(lootstore,id,i+1); + + // TODO: References validity checks +} + +void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const +{ + for(LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr) + { + if(ieItr->mincountOrRef < 0) + { + if(!LootTemplates_Reference.GetLootFor(-ieItr->mincountOrRef)) + LootTemplates_Reference.ReportNotExistedId(-ieItr->mincountOrRef); + else if(ref_set) + ref_set->erase(-ieItr->mincountOrRef); + } + } + + for(LootGroups::const_iterator grItr = Groups.begin(); grItr != Groups.end(); ++grItr) + grItr->CheckLootRefs(store,ref_set); +} + +void LoadLootTemplates_Creature() +{ + LootIdSet ids_set, ids_setUsed; + LootTemplates_Creature.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i ) + { + if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(i)) + { + if(uint32 lootid = cInfo->lootid) + { + if(!ids_set.count(lootid)) + LootTemplates_Creature.ReportNotExistedId(lootid); + else + ids_setUsed.insert(lootid); + } + } + } + for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr) + ids_set.erase(*itr); + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_Creature.ReportUnusedIds(ids_set); +} + +void LoadLootTemplates_Disenchant() +{ + LootIdSet ids_set, ids_setUsed; + LootTemplates_Disenchant.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i ) + { + if(ItemPrototype const* proto = sItemStorage.LookupEntry(i)) + { + if(uint32 lootid = proto->DisenchantID) + { + if(!ids_set.count(lootid)) + LootTemplates_Disenchant.ReportNotExistedId(lootid); + else + ids_setUsed.insert(lootid); + } + } + } + for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr) + ids_set.erase(*itr); + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_Disenchant.ReportUnusedIds(ids_set); +} + +void LoadLootTemplates_Fishing() +{ + LootIdSet ids_set; + LootTemplates_Fishing.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + for(uint32 i = 1; i < sAreaStore.GetNumRows(); ++i ) + { + if(AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(i)) + if(ids_set.count(areaEntry->ID)) + ids_set.erase(areaEntry->ID); + } + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_Fishing.ReportUnusedIds(ids_set); +} + +void LoadLootTemplates_Gameobject() +{ + LootIdSet ids_set, ids_setUsed; + LootTemplates_Gameobject.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i ) + { + if(GameObjectInfo const* gInfo = sGOStorage.LookupEntry(i)) + { + if(uint32 lootid = GameObject::GetLootId(gInfo)) + { + if(!ids_set.count(lootid)) + LootTemplates_Gameobject.ReportNotExistedId(lootid); + else + ids_setUsed.insert(lootid); + } + } + } + for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr) + ids_set.erase(*itr); + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_Gameobject.ReportUnusedIds(ids_set); +} + +void LoadLootTemplates_Item() +{ + LootIdSet ids_set; + LootTemplates_Item.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i ) + if(ItemPrototype const* proto = sItemStorage.LookupEntry(i)) + if(ids_set.count(proto->ItemId)) + ids_set.erase(proto->ItemId); + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_Item.ReportUnusedIds(ids_set); +} + +void LoadLootTemplates_Pickpocketing() +{ + LootIdSet ids_set, ids_setUsed; + LootTemplates_Pickpocketing.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i ) + { + if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(i)) + { + if(uint32 lootid = cInfo->pickpocketLootId) + { + if(!ids_set.count(lootid)) + LootTemplates_Pickpocketing.ReportNotExistedId(lootid); + else + ids_setUsed.insert(lootid); + } + } + } + for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr) + ids_set.erase(*itr); + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_Pickpocketing.ReportUnusedIds(ids_set); +} + +void LoadLootTemplates_Prospecting() +{ + LootIdSet ids_set; + LootTemplates_Prospecting.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i ) + if(ItemPrototype const* proto = sItemStorage.LookupEntry(i)) + if(ids_set.count(proto->ItemId)) + ids_set.erase(proto->ItemId); + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_Prospecting.ReportUnusedIds(ids_set); +} + +void LoadLootTemplates_QuestMail() +{ + LootIdSet ids_set; + LootTemplates_QuestMail.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + ObjectMgr::QuestMap const& questMap = objmgr.GetQuestTemplates(); + for(ObjectMgr::QuestMap::const_iterator itr = questMap.begin(); itr != questMap.end(); ++itr ) + if(ids_set.count(itr->first)) + ids_set.erase(itr->first); + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_QuestMail.ReportUnusedIds(ids_set); +} + +void LoadLootTemplates_Skinning() +{ + LootIdSet ids_set, ids_setUsed; + LootTemplates_Skinning.LoadAndCollectLootIds(ids_set); + + // remove real entries and check existence loot + for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i ) + { + if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(i)) + { + if(uint32 lootid = cInfo->SkinLootId) + { + if(!ids_set.count(lootid)) + LootTemplates_Skinning.ReportNotExistedId(lootid); + else + ids_setUsed.insert(lootid); + } + } + } + for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr) + ids_set.erase(*itr); + + // output error for any still listed (not referenced from appropriate table) ids + LootTemplates_Skinning.ReportUnusedIds(ids_set); +} + +void LoadLootTemplates_Reference() +{ + LootIdSet ids_set; + LootTemplates_Reference.LoadAndCollectLootIds(ids_set); + + // check references and remove used + LootTemplates_Creature.CheckLootRefs(&ids_set); + LootTemplates_Fishing.CheckLootRefs(&ids_set); + LootTemplates_Gameobject.CheckLootRefs(&ids_set); + LootTemplates_Item.CheckLootRefs(&ids_set); + LootTemplates_Pickpocketing.CheckLootRefs(&ids_set); + LootTemplates_Skinning.CheckLootRefs(&ids_set); + LootTemplates_Disenchant.CheckLootRefs(&ids_set); + LootTemplates_Prospecting.CheckLootRefs(&ids_set); + LootTemplates_QuestMail.CheckLootRefs(&ids_set); + LootTemplates_Reference.CheckLootRefs(&ids_set); + + // output error for any still listed ids (not referenced from any loot table) + LootTemplates_Reference.ReportUnusedIds(ids_set); +} diff --git a/src/game/LootMgr.h b/src/game/LootMgr.h new file mode 100644 index 00000000000..fe063a447fc --- /dev/null +++ b/src/game/LootMgr.h @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_LOOTMGR_H +#define MANGOS_LOOTMGR_H + +#include "ItemEnchantmentMgr.h" +#include "ByteBuffer.h" +#include "Utilities/LinkedReference/RefManager.h" + +#include +#include + +enum RollType +{ + ROLL_PASS = 0, + ROLL_NEED = 1, + ROLL_GREED = 2 +}; + +#define MAX_NR_LOOT_ITEMS 16 +// note: the client cannot show more than 16 items total +#define MAX_NR_QUEST_ITEMS 32 +// unrelated to the number of quest items shown, just for reserve + +enum LootMethod +{ + FREE_FOR_ALL = 0, + ROUND_ROBIN = 1, + MASTER_LOOT = 2, + GROUP_LOOT = 3, + NEED_BEFORE_GREED = 4 +}; + +enum PermissionTypes +{ + ALL_PERMISSION = 0, + GROUP_PERMISSION = 1, + MASTER_PERMISSION = 2, + NONE_PERMISSION = 3 +}; + +class Player; +class LootStore; + +struct LootStoreItem +{ + uint32 itemid; // id of the item + float chance; // always positive, chance to drop for both quest and non-quest items, chance to be used for refs + int32 mincountOrRef; // mincount for drop items (positive) or minus referenced TemplateleId (negative) + uint8 group :8; + uint8 maxcount :8; // max drop count for the item (mincountOrRef positive) or Ref multiplicator (mincountOrRef negative) + uint16 conditionId :16; // additional loot condition Id + bool needs_quest :1; // quest drop (negative ChanceOrQuestChance in DB) + + // Constructor, converting ChanceOrQuestChance -> (chance, needs_quest) + // displayid is filled in IsValid() which must be called after + LootStoreItem(uint32 _itemid, float _chanceOrQuestChance, int8 _group, uint8 _conditionId, int32 _mincountOrRef, uint8 _maxcount) + : itemid(_itemid), chance(fabs(_chanceOrQuestChance)), mincountOrRef(_mincountOrRef), + group(_group), maxcount(_maxcount), conditionId(_conditionId), + needs_quest(_chanceOrQuestChance < 0) {} + + bool Roll() const; // Checks if the entry takes it's chance (at loot generation) + bool IsValid(LootStore const& store, uint32 entry) const; + // Checks correctness of values +}; + +struct LootItem +{ + uint32 itemid; + uint32 randomSuffix; + int32 randomPropertyId; + uint16 conditionId :16; // allow compiler pack structure + uint8 count : 8; + bool is_looted : 1; + bool is_blocked : 1; + bool freeforall : 1; // free for all + bool is_underthreshold : 1; + bool is_counted : 1; + bool needs_quest : 1; // quest drop + + // Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties + // Should be called for non-reference LootStoreItem entries only (mincountOrRef > 0) + explicit LootItem(LootStoreItem const& li); + + // Basic checks for player/item compatibility - if false no chance to see the item in the loot + bool AllowedForPlayer(Player const * player) const; +}; + +struct QuestItem +{ + uint8 index; // position in quest_items; + bool is_looted; + + QuestItem() + : index(0), is_looted(false) {} + + QuestItem(uint8 _index, bool _islooted = false) + : index(_index), is_looted(_islooted) {} +}; + +struct Loot; +class LootTemplate; + +typedef std::vector QuestItemList; +typedef std::map QuestItemMap; +typedef std::vector LootStoreItemList; +typedef HM_NAMESPACE::hash_map LootTemplateMap; + +typedef std::set LootIdSet; + +class LootStore +{ + public: + explicit LootStore(char const* name, char const* entryName) : m_name(name), m_entryName(entryName) {} + virtual ~LootStore() { Clear(); } + + void Verify() const; + + void LoadAndCollectLootIds(LootIdSet& ids_set); + void CheckLootRefs(LootIdSet* ref_set = NULL) const;// check existence reference and remove it from ref_set + void ReportUnusedIds(LootIdSet const& ids_set) const; + void ReportNotExistedId(uint32 id) const; + + bool HaveLootFor(uint32 loot_id) const { return m_LootTemplates.find(loot_id) != m_LootTemplates.end(); } + bool HaveQuestLootFor(uint32 loot_id) const; + bool HaveQuestLootForPlayer(uint32 loot_id,Player* player) const; + + LootTemplate const* GetLootFor(uint32 loot_id) const; + + char const* GetName() const { return m_name; } + char const* GetEntryName() const { return m_entryName; } + protected: + void LoadLootTable(); + void Clear(); + private: + LootTemplateMap m_LootTemplates; + char const* m_name; + char const* m_entryName; +}; + +class LootTemplate +{ + class LootGroup; // A set of loot definitions for items (refs are not allowed inside) + typedef std::vector LootGroups; + + public: + // Adds an entry to the group (at loading stage) + void AddEntry(LootStoreItem& item); + // Rolls for every item in the template and adds the rolled items the the loot + void Process(Loot& loot, LootStore const& store, uint8 GroupId = 0) const; + + // True if template includes at least 1 quest drop entry + bool HasQuestDrop(LootTemplateMap const& store, uint8 GroupId = 0) const; + // True if template includes at least 1 quest drop for an active quest of the player + bool HasQuestDropForPlayer(LootTemplateMap const& store, Player const * player, uint8 GroupId = 0) const; + + // Checks integrity of the template + void Verify(LootStore const& store, uint32 Id) const; + void CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const; + private: + LootStoreItemList Entries; // not grouped only + LootGroups Groups; // groups have own (optimised) processing, grouped entries go there +}; + +//===================================================== + +class LootValidatorRef : public Reference +{ + public: + LootValidatorRef() {} + void targetObjectDestroyLink() {} + void sourceObjectDestroyLink() {} +}; + +//===================================================== + +class LootValidatorRefManager : public RefManager +{ + public: + typedef LinkedListHead::Iterator< LootValidatorRef > iterator; + + LootValidatorRef* getFirst() { return (LootValidatorRef*)RefManager::getFirst(); } + LootValidatorRef* getLast() { return (LootValidatorRef*)RefManager::getLast(); } + + iterator begin() { return iterator(getFirst()); } + iterator end() { return iterator(NULL); } + iterator rbegin() { return iterator(getLast()); } + iterator rend() { return iterator(NULL); } +}; + +//===================================================== + +struct Loot +{ + QuestItemMap const& GetPlayerQuestItems() const { return PlayerQuestItems; } + QuestItemMap const& GetPlayerFFAItems() const { return PlayerFFAItems; } + QuestItemMap const& GetPlayerNonQuestNonFFAConditionalItems() const { return PlayerNonQuestNonFFAConditionalItems; } + + QuestItemList* FillFFALoot(Player* player); + QuestItemList* FillQuestLoot(Player* player); + QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player); + + std::vector items; + std::vector quest_items; + uint32 gold; + uint8 unlootedCount; + + Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0) {} + ~Loot() { clear(); } + + // if loot becomes invalid this reference is used to inform the listener + void addLootValidatorRef(LootValidatorRef* pLootValidatorRef) + { + i_LootValidatorRefManager.insertFirst(pLootValidatorRef); + } + + // void clear(); + void clear() + { + items.clear(); gold = 0; PlayersLooting.clear(); + for (QuestItemMap::iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr) + delete itr->second; + for (QuestItemMap::iterator itr = PlayerFFAItems.begin(); itr != PlayerFFAItems.end(); ++itr) + delete itr->second; + for (QuestItemMap::iterator itr = PlayerNonQuestNonFFAConditionalItems.begin(); itr != PlayerNonQuestNonFFAConditionalItems.end(); ++itr) + delete itr->second; + + PlayerQuestItems.clear(); + PlayerFFAItems.clear(); + PlayerNonQuestNonFFAConditionalItems.clear(); + + items.clear(); + quest_items.clear(); + gold = 0; + unlootedCount = 0; + i_LootValidatorRefManager.clearReferences(); + } + + bool empty() const { return items.empty() && gold == 0; } + bool isLooted() const { return gold == 0 && unlootedCount == 0; } + + void NotifyItemRemoved(uint8 lootIndex); + void NotifyQuestItemRemoved(uint8 questIndex); + void NotifyMoneyRemoved(); + void AddLooter(uint64 GUID) { PlayersLooting.insert(GUID); } + void RemoveLooter(uint64 GUID) { PlayersLooting.erase(GUID); } + + void generateMoneyLoot(uint32 minAmount, uint32 maxAmount); + void FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner); + + // Inserts the item into the loot (called by LootTemplate processors) + void AddItem(LootStoreItem const & item); + + LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL); + private: + std::set PlayersLooting; + QuestItemMap PlayerQuestItems; + QuestItemMap PlayerFFAItems; + QuestItemMap PlayerNonQuestNonFFAConditionalItems; + + // All rolls are registered here. They need to know, when the loot is not valid anymore + LootValidatorRefManager i_LootValidatorRefManager; + +}; + +struct LootView +{ + Loot &loot; + QuestItemList *qlist; + QuestItemList *ffalist; + QuestItemList *conditionallist; + Player *viewer; + PermissionTypes permission; + LootView(Loot &_loot, QuestItemList *_qlist, QuestItemList *_ffalist, QuestItemList *_conditionallist, Player *_viewer,PermissionTypes _permission = ALL_PERMISSION) + : loot(_loot), qlist(_qlist), ffalist(_ffalist), conditionallist(_conditionallist), viewer(_viewer), permission(_permission) {} +}; + +extern LootStore LootTemplates_Creature; +extern LootStore LootTemplates_Fishing; +extern LootStore LootTemplates_Gameobject; +extern LootStore LootTemplates_Item; +extern LootStore LootTemplates_Pickpocketing; +extern LootStore LootTemplates_Skinning; +extern LootStore LootTemplates_Disenchant; +extern LootStore LootTemplates_Prospecting; +extern LootStore LootTemplates_QuestMail; + +void LoadLootTemplates_Creature(); +void LoadLootTemplates_Fishing(); +void LoadLootTemplates_Gameobject(); +void LoadLootTemplates_Item(); +void LoadLootTemplates_Pickpocketing(); +void LoadLootTemplates_Skinning(); +void LoadLootTemplates_Disenchant(); +void LoadLootTemplates_Prospecting(); +void LoadLootTemplates_QuestMail(); +void LoadLootTemplates_Reference(); + +inline void LoadLootTables() +{ + LoadLootTemplates_Creature(); + LoadLootTemplates_Fishing(); + LoadLootTemplates_Gameobject(); + LoadLootTemplates_Item(); + LoadLootTemplates_Pickpocketing(); + LoadLootTemplates_Skinning(); + LoadLootTemplates_Disenchant(); + LoadLootTemplates_Prospecting(); + LoadLootTemplates_QuestMail(); + LoadLootTemplates_Reference(); +} + +ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li); +ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv); +#endif diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp new file mode 100644 index 00000000000..a97ed9470a7 --- /dev/null +++ b/src/game/Mail.cpp @@ -0,0 +1,838 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Mail.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 "Unit.h" +#include "Language.h" +#include "Database/DBCStores.h" + +void MailItem::deleteItem( bool inDB ) +{ + if(item) + { + if(inDB) + CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", item->GetGUIDLow()); + + delete item; + item=NULL; + } +} + +void WorldSession::HandleSendMail(WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+1+1+1+4+4+1+4+4+8+1); + + uint64 mailbox, unk3; + std::string receiver, subject, body; + uint32 unk1, unk2, money, COD; + uint8 unk4; + recv_data >> mailbox; + recv_data >> receiver; + + // recheck + CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+1+1+4+4+1+4+4+8+1); + + recv_data >> subject; + + // recheck + CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+1+4+4+1+4+4+8+1); + + recv_data >> body; + + // recheck + CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+(body.size()+1)+4+4+1+4+4+8+1); + + recv_data >> unk1; // stationery? + recv_data >> unk2; // 0x00000000 + + MailItemsInfo mi; + + uint8 items_count; + recv_data >> items_count; // attached items count + + if(items_count > 12) // client limit + return; + + // recheck + CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+(body.size()+1)+4+4+1+items_count*(1+8)+4+4+8+1); + + if(items_count) + { + for(uint8 i = 0; i < items_count; ++i) + { + uint8 item_slot; + uint64 item_guid; + recv_data >> item_slot; + recv_data >> item_guid; + mi.AddItem(GUID_LOPART(item_guid), item_slot); + } + } + + recv_data >> money >> COD; // money and cod + recv_data >> unk3; // const 0 + recv_data >> unk4; // const 0 + + items_count = mi.size(); // this is the real size after the duplicates have been removed + + if (receiver.empty()) + return; + + Player* pl = _player; + + uint64 rc = 0; + if(normalizePlayerName(receiver)) + rc = objmgr.GetPlayerGUIDByName(receiver); + + if (!rc) + { + sLog.outDetail("Player %u is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", + pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); + pl->SendMailResult(0, 0, MAIL_ERR_RECIPIENT_NOT_FOUND); + return; + } + + sLog.outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); + + if(pl->GetGUID() == rc) + { + pl->SendMailResult(0, 0, MAIL_ERR_CANNOT_SEND_TO_SELF); + return; + } + + uint32 reqmoney = money + 30; + if (items_count) + reqmoney = money + (30 * items_count); + + if (pl->GetMoney() < reqmoney) + { + pl->SendMailResult(0, 0, MAIL_ERR_NOT_ENOUGH_MONEY); + return; + } + + Player *receive = objmgr.GetPlayer(rc); + + uint32 rc_team = 0; + uint8 mails_count = 0; //do not allow to send to one player more than 100 mails + + if(receive) + { + rc_team = receive->GetTeam(); + mails_count = receive->GetMailSize(); + } + else + { + rc_team = objmgr.GetPlayerTeamByGUID(rc); + QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc)); + if(result) + { + Field *fields = result->Fetch(); + mails_count = fields[0].GetUInt32(); + delete result; + } + } + //do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. + if (mails_count > 100) + { + pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); + return; + } + // test the receiver's Faction... + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) + { + pl->SendMailResult(0, 0, MAIL_ERR_NOT_YOUR_TEAM); + return; + } + + if (items_count) + { + for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) + { + MailItem& mailItem = mailItemIter->second; + + if(!mailItem.item_guidlow) + { + pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); + return; + } + + mailItem.item = pl->GetItemByGuid(MAKE_NEW_GUID(mailItem.item_guidlow, 0, HIGHGUID_ITEM)); + // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) + if(!mailItem.item || !mailItem.item->CanBeTraded()) + { + pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); + return; + } + if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION)) + { + pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); + return; + } + } + } + pl->SendMailResult(0, 0, MAIL_OK); + + uint32 itemTextId = 0; + if (!body.empty()) + { + itemTextId = objmgr.CreateItemText( body ); + } + + pl->ModifyMoney( -int32(reqmoney) ); + + bool needItemDelay = false; + + if(items_count > 0 || money > 0) + { + uint32 rc_account = 0; + if(receive) + rc_account = receive->GetSession()->GetAccountId(); + else + rc_account = objmgr.GetPlayerAccountIdByGUID(rc); + + if (items_count > 0) + { + for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) + { + MailItem& mailItem = mailItemIter->second; + if(!mailItem.item) + continue; + + mailItem.item_template = mailItem.item ? mailItem.item->GetEntry() : 0; + + if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) + { + sLog.outCommand("GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", + GetPlayerName(), GetAccountId(), mailItem.item->GetProto()->Name1, mailItem.item->GetEntry(), mailItem.item->GetCount(), receiver.c_str(), rc_account); + } + + pl->MoveItemFromInventory(mailItem.item->GetBagSlot(), mailItem.item->GetSlot(), true); + CharacterDatabase.BeginTransaction(); + mailItem.item->DeleteFromInventoryDB(); //deletes item from character's inventory + mailItem.item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone + // owner in data will set at mail receive and item extracting + CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), mailItem.item->GetGUIDLow()); + CharacterDatabase.CommitTransaction(); + } + + // if item send to character at another account, then apply item delivery delay + needItemDelay = pl->GetSession()->GetAccountId() != rc_account; + } + + if(money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + sLog.outCommand("GM %s (Account: %u) mail money: %u to player: %s (Account: %u)", + GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); + } + } + + // If theres is an item, there is a one hour delivery delay if sent to another account's character. + uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; + + // will delete item or place to receiver mail list + WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, pl->GetGUIDLow(), GUID_LOPART(rc), subject, itemTextId, &mi, money, COD, MAIL_CHECK_MASK_NONE, deliver_delay); + + CharacterDatabase.BeginTransaction(); + pl->SaveInventoryAndGoldToDB(); + CharacterDatabase.CommitTransaction(); +} + +//called when mail is read +void WorldSession::HandleMarkAsRead(WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint64 mailbox; + uint32 mailId; + recv_data >> mailbox; + recv_data >> mailId; + Player *pl = _player; + Mail *m = pl->GetMail(mailId); + if (m) + { + if (pl->unReadMails) + --pl->unReadMails; + m->checked = m->checked | MAIL_CHECK_MASK_READ; + // m->expire_time = time(NULL) + (30 * DAY); // Expire time do not change at reading mail + pl->m_mailsUpdated = true; + m->state = MAIL_STATE_CHANGED; + } +} + +//called when client deletes mail +void WorldSession::HandleMailDelete(WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint64 mailbox; + uint32 mailId; + recv_data >> mailbox; + recv_data >> mailId; + Player* pl = _player; + pl->m_mailsUpdated = true; + Mail *m = pl->GetMail(mailId); + if(m) + m->state = MAIL_STATE_DELETED; + pl->SendMailResult(mailId, MAIL_DELETED, 0); +} + +void WorldSession::HandleReturnToSender(WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint64 mailbox; + uint32 mailId; + recv_data >> mailbox; + recv_data >> mailId; + Player *pl = _player; + Mail *m = pl->GetMail(mailId); + if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) + { + pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_ERR_INTERNAL_ERROR); + return; + } + //we can return mail now + //so firstly delete the old one + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mailId); + // needed? + CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mailId); + CharacterDatabase.CommitTransaction(); + pl->RemoveMail(mailId); + + MailItemsInfo mi; + + if(m->HasItems()) + { + for(std::vector::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2) + { + Item *item = pl->GetMItem(itr2->item_guid); + if(item) + mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item); + else + { + //WTF? + } + + pl->RemoveMItem(itr2->item_guid); + } + } + + SendReturnToSender(MAIL_NORMAL, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, 0, m->mailTemplateId); + + delete m; //we can deallocate old mail + pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, 0); +} + +void WorldSession::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 ) +{ + if(messageType != MAIL_NORMAL) // return only to players + { + mi->deleteIncludedItems(true); + return; + } + + Player *receiver = objmgr.GetPlayer(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER)); + + uint32 rc_account = 0; + if(!receiver) + rc_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER)); + + if(!receiver && !rc_account) // sender not exist + { + mi->deleteIncludedItems(true); + return; + } + + // preper mail and send in other case + bool needItemDelay = false; + + if(mi && !mi->empty()) + { + // if item send to character at another account, then apply item delivery delay + needItemDelay = sender_acc != rc_account; + + // set owner to new receiver (to prevent delete item with sender char deleting) + CharacterDatabase.BeginTransaction(); + for(MailItemMap::iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter) + { + MailItem& mailItem = mailItemIter->second; + mailItem.item->SaveToDB(); // item not in inventory and can be save standalone + // owner in data will set at mail receive and item extracting + CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", receiver_guid, mailItem.item->GetGUIDLow()); + } + CharacterDatabase.CommitTransaction(); + } + + // If theres is an item, there is a one hour delivery delay. + uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; + + // will delete item or place to receiver mail list + WorldSession::SendMailTo(receiver, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, sender_guid, receiver_guid, subject, itemTextId, mi, money, 0, MAIL_CHECK_MASK_RETURNED,deliver_delay,mailTemplateId); +} + +//called when player takes item attached in mail +void WorldSession::HandleTakeItem(WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4+4); + + uint64 mailbox; + uint32 mailId; + uint32 itemId; + recv_data >> mailbox; + recv_data >> mailId; + recv_data >> itemId; // item guid low? + Player* pl = _player; + + Mail* m = pl->GetMail(mailId); + if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) + { + pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); + return; + } + + // prevent cheating with skip client money check + if(pl->GetMoney() < m->COD) + { + pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY); + return; + } + + Item *it = pl->GetMItem(itemId); + + ItemPosCountVec dest; + uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, it, false ); + if( msg == EQUIP_ERR_OK ) + { + m->RemoveItem(itemId); + m->removedItems.push_back(itemId); + + if (m->COD > 0) //if there is COD, take COD money from player and send them to sender by mail + { + uint64 sender_guid = MAKE_NEW_GUID(m->sender, 0, HIGHGUID_PLAYER); + Player *receive = objmgr.GetPlayer(sender_guid); + + uint32 sender_accId = 0; + + if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) + { + std::string sender_name; + if(receive) + { + sender_accId = receive->GetSession()->GetAccountId(); + sender_name = receive->GetName(); + } + else + { + // can be calculated early + sender_accId = objmgr.GetPlayerAccountIdByGUID(sender_guid); + + if(!objmgr.GetPlayerNameByGUID(sender_guid,sender_name)) + sender_name = objmgr.GetMangosStringForDBCLocale(LANG_UNKNOWN); + } + sLog.outCommand("GM %s (Account: %u) receive mail item: %s (Entry: %u Count: %u) and send COD money: %u to player: %s (Account: %u)", + GetPlayerName(),GetAccountId(),it->GetProto()->Name1,it->GetEntry(),it->GetCount(),m->COD,sender_name.c_str(),sender_accId); + } + else if(!receive) + sender_accId = objmgr.GetPlayerAccountIdByGUID(sender_guid); + + // check player existanse + if(receive || sender_accId) + { + WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, m->receiver, m->sender, m->subject, 0, NULL, m->COD, 0, MAIL_CHECK_MASK_COD_PAYMENT); + } + + pl->ModifyMoney( -int32(m->COD) ); + } + m->COD = 0; + m->state = MAIL_STATE_CHANGED; + pl->m_mailsUpdated = true; + pl->RemoveMItem(it->GetGUIDLow()); + + uint32 count = it->GetCount(); // save counts before store and possible merge with deleting + pl->MoveItemToInventory(dest,it,true); + + CharacterDatabase.BeginTransaction(); + pl->SaveInventoryAndGoldToDB(); + pl->_SaveMail(); + CharacterDatabase.CommitTransaction(); + + pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count); + } + else + pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_BAG_FULL, msg); +} + +void WorldSession::HandleTakeMoney(WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint64 mailbox; + uint32 mailId; + recv_data >> mailbox; + recv_data >> mailId; + Player *pl = _player; + + Mail* m = pl->GetMail(mailId); + if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) + { + pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_INTERNAL_ERROR); + return; + } + + pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, 0); + + pl->ModifyMoney(m->money); + m->money = 0; + m->state = MAIL_STATE_CHANGED; + pl->m_mailsUpdated = true; + + // save money and mail to prevent cheating + CharacterDatabase.BeginTransaction(); + pl->SetUInt32ValueInDB(PLAYER_FIELD_COINAGE,pl->GetMoney(),pl->GetGUID()); + pl->_SaveMail(); + CharacterDatabase.CommitTransaction(); +} + +//called when player lists his received mails +void WorldSession::HandleGetMail(WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + uint64 mailbox; + recv_data >> mailbox; + + //GameObject* obj = ObjectAccessor::GetGameObject(_player, mailbox); + //if(!obj || !obj->IsMailBox()) + // return; + + Player* pl = _player; + + //load players mails, and mailed items + if(!pl->m_mailsLoaded) + pl ->_LoadMail(); + + // client can't work with packets > max int16 value + const uint32 maxPacketSize = 32767; + + uint32 mails_count = 0; // real send to client mails amount + + WorldPacket data(SMSG_MAIL_LIST_RESULT, (200)); // guess size + data << uint8(0); // mail's count + time_t cur_time = time(NULL); + + for(PlayerMails::iterator itr = pl->GetmailBegin(); itr != pl->GetmailEnd(); ++itr) + { + // skip deleted or not delivered (deliver delay not expired) mails + if ((*itr)->state == MAIL_STATE_DELETED || cur_time < (*itr)->deliver_time) + continue; + + uint8 item_count = (*itr)->items.size(); // max count is MAX_MAIL_ITEMS (12) + + size_t next_mail_size = 2+4+1+8+4*8+((*itr)->subject.size()+1)+1+item_count*(1+4+4+6*3*4+4+4+1+4+4+4); + + if(data.wpos()+next_mail_size > maxPacketSize) + break; + + data << (uint16) 0x0040; // unknown 2.3.0, different values + data << (uint32) (*itr)->messageID; // Message ID + data << (uint8) (*itr)->messageType; // Message Type + + switch((*itr)->messageType) + { + case MAIL_NORMAL: // sender guid + data << uint64(MAKE_NEW_GUID((*itr)->sender, 0, HIGHGUID_PLAYER)); + break; + case MAIL_CREATURE: + case MAIL_GAMEOBJECT: + case MAIL_AUCTION: + data << (uint32) (*itr)->sender; // creature/gameobject entry, auction id + break; + case MAIL_ITEM: // item entry (?) sender = "Unknown", NYI + break; + } + + data << (uint32) (*itr)->COD; // COD + data << (uint32) (*itr)->itemTextId; // sure about this + data << (uint32) 0; // unknown + data << (uint32) (*itr)->stationery; // stationery (Stationery.dbc) + data << (uint32) (*itr)->money; // Gold + data << (uint32) 0x04; // unknown, 0x4 - auction, 0x10 - normal + // Time + data << (float) ((*itr)->expire_time-time(NULL))/DAY; + data << (uint32) (*itr)->mailTemplateId; // mail template (MailTemplate.dbc) + data << (*itr)->subject; // Subject string - once 00, when mail type = 3 + + data << (uint8) item_count; + + for(uint8 i = 0; i < item_count; ++i) + { + Item *item = pl->GetMItem((*itr)->items[i].item_guid); + // item index (0-6?) + data << (uint8) i; + // item guid low? + data << (uint32) (item ? item->GetGUIDLow() : 0); + // entry + data << (uint32) (item ? item->GetEntry() : 0); + for(uint8 j = 0; j < 6; ++j) + { + // unsure + data << (uint32) (item ? item->GetEnchantmentCharges((EnchantmentSlot)j) : 0); + // unsure + data << (uint32) (item ? item->GetEnchantmentDuration((EnchantmentSlot)j) : 0); + // unsure + data << (uint32) (item ? item->GetEnchantmentId((EnchantmentSlot)j) : 0); + } + // can be negative + data << (uint32) (item ? item->GetItemRandomPropertyId() : 0); + // unk + data << (uint32) (item ? item->GetItemSuffixFactor() : 0); + // stack count + data << (uint8) (item ? item->GetCount() : 0); + // charges + data << (uint32) (item ? item->GetSpellCharges() : 0); + // durability + data << (uint32) (item ? item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) : 0); + // durability + data << (uint32) (item ? item->GetUInt32Value(ITEM_FIELD_DURABILITY) : 0); + } + + mails_count += 1; + } + + data.put(0, mails_count); // set real send mails to client + SendPacket(&data); + + // recalculate m_nextMailDelivereTime and unReadMails + _player->UpdateNextMailTimeAndUnreads(); +} + +///this function is called when client needs mail message body, or when player clicks on item which has ITEM_FIELD_ITEM_TEXT_ID > 0 +void WorldSession::HandleItemTextQuery(WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+4+4); + + uint32 itemTextId; + uint32 mailId; //this value can be item id in bag, but it is also mail id + uint32 unk; //maybe something like state - 0x70000000 + + recv_data >> itemTextId >> mailId >> unk; + + //some check needed, if player has item with guid mailId, or has mail with id mailId + + sLog.outDebug("CMSG_ITEM_TEXT_QUERY itemguid: %u, mailId: %u, unk: %u", itemTextId, mailId, unk); + + WorldPacket data(SMSG_ITEM_TEXT_QUERY_RESPONSE, (4+10));// guess size + data << itemTextId; + data << objmgr.GetItemText( itemTextId ); + SendPacket(&data); +} + +//used when player copies mail body to his inventory +void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint64 mailbox; + uint32 mailId; + + recv_data >> mailbox >> mailId; + + Player *pl = _player; + + Mail* m = pl->GetMail(mailId); + if(!m || !m->itemTextId || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) + { + pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR); + return; + } + + Item *bodyItem = new Item; // This is not bag and then can be used new Item. + if(!bodyItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), MAIL_BODY_ITEM_TEMPLATE, pl)) + { + delete bodyItem; + return; + } + + bodyItem->SetUInt32Value( ITEM_FIELD_ITEM_TEXT_ID , m->itemTextId ); + bodyItem->SetUInt32Value( ITEM_FIELD_CREATOR, m->sender); + + sLog.outDetail("HandleMailCreateTextItem mailid=%u",mailId); + + ItemPosCountVec dest; + uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, bodyItem, false ); + if( msg == EQUIP_ERR_OK ) + { + m->itemTextId = 0; + m->state = MAIL_STATE_CHANGED; + pl->m_mailsUpdated = true; + + pl->StoreItem(dest, bodyItem, true); + //bodyItem->SetState(ITEM_NEW, pl); is set automatically + pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, 0); + } + else + { + pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_BAG_FULL, msg); + delete bodyItem; + } +} + +//TODO Fix me! ... this void has probably bad condition, but good data are sent +void WorldSession::HandleMsgQueryNextMailtime(WorldPacket & /*recv_data*/ ) +{ + WorldPacket data(MSG_QUERY_NEXT_MAIL_TIME, 8); + + if(!_player->m_mailsLoaded) + _player->_LoadMail(); + + if( _player->unReadMails > 0 ) + { + data << (uint32) 0; // float + data << (uint32) 0; // count + uint32 count = 0; + for(PlayerMails::iterator itr = _player->GetmailBegin(); itr != _player->GetmailEnd(); ++itr) + { + Mail *m = (*itr); + // not checked yet, already must be delivered + if((m->checked & MAIL_CHECK_MASK_READ)==0 && (m->deliver_time <= time(NULL))) + { + ++count; + + if(count > 2) + { + count = 2; + break; + } + + data << (uint64) m->sender; // sender guid + + switch(m->messageType) + { + case MAIL_AUCTION: + data << (uint32) 2; + data << (uint32) 2; + data << (uint32) m->stationery; + break; + default: + data << (uint32) 0; + data << (uint32) 0; + data << (uint32) m->stationery; + break; + } + data << (uint32) 0xC6000000; // float unk, time or something + } + } + data.put(4, count); + } + else + { + data << (uint32) 0xC7A8C000; + data << (uint32) 0x00000000; + } + SendPacket(&data); +} + +void WorldSession::SendMailTo(Player* receiver, uint8 messageType, uint8 stationery, uint32 sender_guidlow_or_entry, uint32 receiver_guidlow, std::string subject, uint32 itemTextId, MailItemsInfo* mi, uint32 money, uint32 COD, uint32 checked, uint32 deliver_delay, uint16 mailTemplateId) +{ + uint32 mailId = objmgr.GenerateMailID(); + + time_t deliver_time = time(NULL) + deliver_delay; + + //expire time if COD 3 days, if no COD 30 days, if auction sale pending 1 hour + uint32 expire_delay; + if(messageType == MAIL_AUCTION && !mi && !money) // auction mail without any items and money + expire_delay = HOUR; + else + expire_delay = (COD > 0) ? 3*DAY : 30*DAY; + + time_t expire_time = deliver_time + expire_delay; + + if(mailTemplateId && !sMailTemplateStore.LookupEntry(mailTemplateId)) + { + sLog.outError( "WorldSession::SendMailTo - Mail have not existed MailTemplateId (%u), remove at send", mailTemplateId); + mailTemplateId = 0; + } + + if(receiver) + { + receiver->AddNewMailDeliverTime(deliver_time); + + if ( receiver->IsMailsLoaded() ) + { + Mail * m = new Mail; + m->messageID = mailId; + m->messageType = messageType; + m->stationery = stationery; + m->mailTemplateId = mailTemplateId; + m->sender = sender_guidlow_or_entry; + m->receiver = receiver->GetGUIDLow(); + m->subject = subject; + m->itemTextId = itemTextId; + + if(mi) + m->AddAllItems(*mi); + + m->expire_time = expire_time; + m->deliver_time = deliver_time; + m->money = money; + m->COD = COD; + m->checked = checked; + m->state = MAIL_STATE_UNCHANGED; + + receiver->AddMail(m); //to insert new mail to beginning of maillist + + if(mi) + { + for(MailItemMap::iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter) + { + MailItem& mailItem = mailItemIter->second; + if(mailItem.item) + receiver->AddMItem(mailItem.item); + } + } + } + else if(mi) + mi->deleteIncludedItems(); + } + else if(mi) + mi->deleteIncludedItems(); + + CharacterDatabase.BeginTransaction(); + CharacterDatabase.escape_string(subject); + CharacterDatabase.PExecute("INSERT INTO mail (id,messageType,stationery,mailTemplateId,sender,receiver,subject,itemTextId,has_items,expire_time,deliver_time,money,cod,checked) " + "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%s', '%u', '%u', '" I64FMTD "','" I64FMTD "', '%u', '%u', '%d')", + mailId, messageType, stationery, mailTemplateId, sender_guidlow_or_entry, receiver_guidlow, subject.c_str(), itemTextId, (mi && !mi->empty() ? 1 : 0), (uint64)expire_time, (uint64)deliver_time, money, COD, checked); + + if(mi) + { + for(MailItemMap::const_iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter) + { + MailItem const& mailItem = mailItemIter->second; + CharacterDatabase.PExecute("INSERT INTO mail_items (mail_id,item_guid,item_template,receiver) VALUES ('%u', '%u', '%u','%u')", mailId, mailItem.item_guidlow, mailItem.item_template,receiver_guidlow); + } + } + CharacterDatabase.CommitTransaction(); +} diff --git a/src/game/Mail.h b/src/game/Mail.h new file mode 100644 index 00000000000..bec54eb41f7 --- /dev/null +++ b/src/game/Mail.h @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef MANGOS_MAIL_H +#define MANGOS_MAIL_H + +#include "Common.h" +#include + +class Item; + +#define MAIL_BODY_ITEM_TEMPLATE 8383 // - plain letter, A Dusty Unsent Letter: 889 +#define MAX_MAIL_ITEMS 12 + +enum MAIL_RESPONSE +{ + MAIL_OK = 0, + MAIL_MONEY_TAKEN = 1, + MAIL_ITEM_TAKEN = 2, + MAIL_RETURNED_TO_SENDER = 3, + MAIL_DELETED = 4, + MAIL_MADE_PERMANENT = 5 +}; + +enum MAIL_ERRORS +{ + MAIL_ERR_BAG_FULL = 1, + MAIL_ERR_CANNOT_SEND_TO_SELF = 2, + MAIL_ERR_NOT_ENOUGH_MONEY = 3, + MAIL_ERR_RECIPIENT_NOT_FOUND = 4, + MAIL_ERR_NOT_YOUR_TEAM = 5, + MAIL_ERR_INTERNAL_ERROR = 6, + MAIL_ERR_DISABLED_FOR_TRIAL_ACC = 14, + MAIL_ERR_RECIPIENT_CAP_REACHED = 15, + MAIL_ERR_CANT_SEND_WRAPPED_COD = 16, + MAIL_ERR_MAIL_AND_CHAT_SUSPENDED = 17 +}; + +enum MailCheckMask +{ + MAIL_CHECK_MASK_NONE = 0, + MAIL_CHECK_MASK_READ = 1, + MAIL_CHECK_MASK_AUCTION = 4, + MAIL_CHECK_MASK_COD_PAYMENT = 8, + MAIL_CHECK_MASK_RETURNED = 16 +}; + +enum MailMessageType +{ + MAIL_NORMAL = 0, + MAIL_AUCTION = 2, + MAIL_CREATURE = 3, // client send CMSG_CREATURE_QUERY on this mailmessagetype + MAIL_GAMEOBJECT = 4, // client send CMSG_GAMEOBJECT_QUERY on this mailmessagetype + MAIL_ITEM = 5, // client send CMSG_ITEM_QUERY on this mailmessagetype +}; + +enum MailState +{ + MAIL_STATE_UNCHANGED = 1, + MAIL_STATE_CHANGED = 2, + MAIL_STATE_DELETED = 3 +}; + +enum MailAuctionAnswers +{ + AUCTION_OUTBIDDED = 0, + AUCTION_WON = 1, + AUCTION_SUCCESSFUL = 2, + AUCTION_EXPIRED = 3, + AUCTION_CANCELLED_TO_BIDDER = 4, + AUCTION_CANCELED = 5, + AUCTION_SALE_PENDING = 6 +}; + +// gathered from Stationery.dbc +enum MailStationery +{ + MAIL_STATIONERY_UNKNOWN = 0x01, + MAIL_STATIONERY_NORMAL = 0x29, + MAIL_STATIONERY_GM = 0x3D, + MAIL_STATIONERY_AUCTION = 0x3E, + MAIL_STATIONERY_VAL = 0x40, + MAIL_STATIONERY_CHR = 0x41 +}; + +struct MailItemInfo +{ + uint32 item_guid; + uint32 item_template; +}; + +struct MailItem +{ + MailItem() : item_slot(0), item_guidlow(0), item_template(0), item(NULL) {} + + uint8 item_slot; // slot in mail + uint32 item_guidlow; // item guid (low part) + uint32 item_template; // item entry + Item *item; // item pointer + + void deleteItem(bool inDB = false); +}; + +typedef std::map MailItemMap; + +class MailItemsInfo +{ + public: + MailItemMap::const_iterator begin() const { return i_MailItemMap.begin(); } + MailItemMap::const_iterator end() const { return i_MailItemMap.end(); } + MailItemMap::iterator begin() { return i_MailItemMap.begin(); } + MailItemMap::iterator end() { return i_MailItemMap.end(); } + + void AddItem(uint32 guidlow, uint32 _template, Item *item, uint8 slot = 0) + { + MailItem mailItem; + mailItem.item_slot = slot; + mailItem.item_guidlow = guidlow; + mailItem.item_template = _template; + mailItem.item = item; + i_MailItemMap[guidlow] = mailItem; + } + + void AddItem(uint32 guidlow, uint8 slot = 0) + { + MailItem mailItem; + mailItem.item_guidlow = guidlow; + mailItem.item_slot = slot; + i_MailItemMap[guidlow] = mailItem; + } + + uint8 size() const { return i_MailItemMap.size(); } + bool empty() const { return i_MailItemMap.empty(); } + + void deleteIncludedItems(bool inDB = false) + { + for(MailItemMap::iterator mailItemIter = begin(); mailItemIter != end(); ++mailItemIter) + { + MailItem& mailItem = mailItemIter->second; + mailItem.deleteItem(inDB); + } + } + private: + MailItemMap i_MailItemMap; // Keep the items in a map to avoid duplicate guids (which can happen), store only low part of guid +}; + +struct Mail +{ + uint32 messageID; + uint8 messageType; + uint8 stationery; + uint16 mailTemplateId; + uint32 sender; + uint32 receiver; + std::string subject; + uint32 itemTextId; + std::vector items; + std::vector removedItems; + time_t expire_time; + time_t deliver_time; + uint32 money; + uint32 COD; + uint32 checked; + MailState state; + + void AddItem(uint32 itemGuidLow, uint32 item_template) + { + MailItemInfo mii; + mii.item_guid = itemGuidLow; + mii.item_template = item_template; + items.push_back(mii); + } + + void AddAllItems(MailItemsInfo& pMailItemsInfo) + { + for(MailItemMap::iterator mailItemIter = pMailItemsInfo.begin(); mailItemIter != pMailItemsInfo.end(); ++mailItemIter) + { + MailItem& mailItem = mailItemIter->second; + AddItem(mailItem.item_guidlow, mailItem.item_template); + } + } + + bool RemoveItem(uint32 itemId) + { + for(std::vector::iterator itr = items.begin(); itr != items.end(); ++itr) + { + if(itr->item_guid == itemId) + { + items.erase(itr); + return true; + } + } + return false; + } + + bool HasItems() const { return !items.empty(); } +}; +#endif diff --git a/src/game/Makefile.am b/src/game/Makefile.am new file mode 100644 index 00000000000..cffd1f7b093 --- /dev/null +++ b/src/game/Makefile.am @@ -0,0 +1,278 @@ +# Copyright (C) 2005-2008 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## CPP flags for includes, defines, etc. +AM_CPPFLAGS = + +## Build MaNGOS game library as convenience library. +# All libraries will be convenience libraries. Might be changed to shared +# later. +noinst_LIBRARIES = libgame.a + +libgame_a_CPPFLAGS = \ +$(MYSQL_INCLUDES) \ +$(POSTGRE_INCLUDES) \ +-I$(top_srcdir)/dep/include \ +-I$(top_srcdir)/src/framework \ +-I$(top_srcdir)/src/shared \ +-I$(top_srcdir)/src/shared/vmap + +# libmangossgame library will later be reused by ... +libgame_a_SOURCES = \ +$(srcdir)/AccountMgr.cpp \ +$(srcdir)/AccountMgr.h \ +$(srcdir)/AddonHandler.cpp \ +$(srcdir)/AddonHandler.h \ +$(srcdir)/AggressorAI.cpp \ +$(srcdir)/AggressorAI.h \ +$(srcdir)/AnimalRandomMovementGenerator.h \ +$(srcdir)/ArenaTeam.cpp \ +$(srcdir)/ArenaTeam.h \ +$(srcdir)/ArenaTeamHandler.cpp \ +$(srcdir)/AuctionHouse.cpp \ +$(srcdir)/AuctionHouseObject.h \ +$(srcdir)/Bag.cpp \ +$(srcdir)/Bag.h \ +$(srcdir)/BattleGround.cpp \ +$(srcdir)/BattleGroundAA.cpp \ +$(srcdir)/BattleGroundAB.cpp \ +$(srcdir)/BattleGroundAV.cpp \ +$(srcdir)/BattleGroundBE.cpp \ +$(srcdir)/BattleGroundEY.cpp \ +$(srcdir)/BattleGroundNA.cpp \ +$(srcdir)/BattleGroundRL.cpp \ +$(srcdir)/BattleGroundWS.cpp \ +$(srcdir)/BattleGround.h \ +$(srcdir)/BattleGroundAA.h \ +$(srcdir)/BattleGroundAB.h \ +$(srcdir)/BattleGroundAV.h \ +$(srcdir)/BattleGroundBE.h \ +$(srcdir)/BattleGroundEY.h \ +$(srcdir)/BattleGroundNA.h \ +$(srcdir)/BattleGroundRL.h \ +$(srcdir)/BattleGroundWS.h \ +$(srcdir)/BattleGroundHandler.cpp \ +$(srcdir)/BattleGroundMgr.cpp \ +$(srcdir)/BattleGroundMgr.h \ +$(srcdir)/Cell.h \ +$(srcdir)/CellImpl.h \ +$(srcdir)/Channel.cpp \ +$(srcdir)/Channel.h \ +$(srcdir)/ChannelHandler.cpp \ +$(srcdir)/ChannelMgr.h \ +$(srcdir)/CharacterHandler.cpp \ +$(srcdir)/Chat.cpp \ +$(srcdir)/Chat.h \ +$(srcdir)/ChatHandler.cpp \ +$(srcdir)/CombatHandler.cpp \ +$(srcdir)/ConfusedMovementGenerator.cpp \ +$(srcdir)/ConfusedMovementGenerator.h \ +$(srcdir)/Corpse.cpp \ +$(srcdir)/Corpse.h \ +$(srcdir)/CreatureAI.cpp \ +$(srcdir)/CreatureAI.h \ +$(srcdir)/CreatureAIImpl.h \ +$(srcdir)/CreatureAIRegistry.cpp \ +$(srcdir)/CreatureAIRegistry.h \ +$(srcdir)/CreatureAISelector.cpp \ +$(srcdir)/CreatureAISelector.h \ +$(srcdir)/Creature.cpp \ +$(srcdir)/Creature.h \ +$(srcdir)/debugcmds.cpp \ +$(srcdir)/DestinationHolder.cpp \ +$(srcdir)/DestinationHolder.h \ +$(srcdir)/DestinationHolderImp.h \ +$(srcdir)/DuelHandler.cpp \ +$(srcdir)/DynamicObject.cpp \ +$(srcdir)/DynamicObject.h \ +$(srcdir)/FleeingMovementGenerator.cpp \ +$(srcdir)/FleeingMovementGenerator.h \ +$(srcdir)/Formulas.h \ +$(srcdir)/GameEvent.cpp \ +$(srcdir)/GameEvent.h \ +$(srcdir)/GameObject.cpp \ +$(srcdir)/GameObject.h \ +$(srcdir)/GlobalEvents.cpp \ +$(srcdir)/GlobalEvents.h \ +$(srcdir)/GossipDef.cpp \ +$(srcdir)/GossipDef.h \ +$(srcdir)/GridDefines.h \ +$(srcdir)/GridNotifiers.cpp \ +$(srcdir)/GridNotifiers.h \ +$(srcdir)/GridNotifiersImpl.h \ +$(srcdir)/GridStates.cpp \ +$(srcdir)/GridStates.h \ +$(srcdir)/Group.cpp \ +$(srcdir)/Group.h \ +$(srcdir)/GroupHandler.cpp \ +$(srcdir)/GuardAI.cpp \ +$(srcdir)/GuardAI.h \ +$(srcdir)/Guild.cpp \ +$(srcdir)/Guild.h \ +$(srcdir)/GuildHandler.cpp \ +$(srcdir)/HateMatrix.h \ +$(srcdir)/HomeMovementGenerator.cpp \ +$(srcdir)/HomeMovementGenerator.h \ +$(srcdir)/HostilRefManager.cpp \ +$(srcdir)/HostilRefManager.h \ +$(srcdir)/IdleMovementGenerator.cpp \ +$(srcdir)/IdleMovementGenerator.h \ +$(srcdir)/InstanceData.cpp \ +$(srcdir)/InstanceData.h \ +$(srcdir)/InstanceSaveMgr.cpp \ +$(srcdir)/InstanceSaveMgr.h \ +$(srcdir)/Item.cpp \ +$(srcdir)/Item.h \ +$(srcdir)/ItemEnchantmentMgr.cpp \ +$(srcdir)/ItemEnchantmentMgr.h \ +$(srcdir)/ItemHandler.cpp \ +$(srcdir)/ItemPrototype.h \ +$(srcdir)/Language.h \ +$(srcdir)/Level0.cpp \ +$(srcdir)/Level1.cpp \ +$(srcdir)/Level2.cpp \ +$(srcdir)/Level3.cpp \ +$(srcdir)/LFGHandler.cpp \ +$(srcdir)/LootHandler.cpp \ +$(srcdir)/LootMgr.cpp \ +$(srcdir)/LootMgr.h \ +$(srcdir)/Mail.cpp \ +$(srcdir)/Mail.h \ +$(srcdir)/Map.cpp \ +$(srcdir)/Map.h \ +$(srcdir)/MapInstanced.cpp \ +$(srcdir)/MapInstanced.h \ +$(srcdir)/MapManager.cpp \ +$(srcdir)/MapManager.h \ +$(srcdir)/MiscHandler.cpp \ +$(srcdir)/MotionMaster.cpp \ +$(srcdir)/MotionMaster.h \ +$(srcdir)/MovementGenerator.cpp \ +$(srcdir)/MovementGenerator.h \ +$(srcdir)/MovementGeneratorImpl.h \ +$(srcdir)/MovementHandler.cpp \ +$(srcdir)/NPCHandler.cpp \ +$(srcdir)/NPCHandler.h \ +$(srcdir)/NullCreatureAI.cpp \ +$(srcdir)/NullCreatureAI.h \ +$(srcdir)/ObjectAccessor.cpp \ +$(srcdir)/ObjectAccessor.h \ +$(srcdir)/Object.cpp \ +$(srcdir)/ObjectDefines.h \ +$(srcdir)/ObjectGridLoader.cpp \ +$(srcdir)/ObjectGridLoader.h \ +$(srcdir)/Object.h \ +$(srcdir)/ObjectMgr.cpp \ +$(srcdir)/ObjectMgr.h \ +$(srcdir)/ObjectPosSelector.cpp \ +$(srcdir)/ObjectPosSelector.h \ +$(srcdir)/Opcodes.cpp \ +$(srcdir)/Opcodes.h \ +$(srcdir)/Path.h \ +$(srcdir)/PetAI.cpp \ +$(srcdir)/PetAI.h \ +$(srcdir)/Pet.cpp \ +$(srcdir)/Pet.h \ +$(srcdir)/PetHandler.cpp \ +$(srcdir)/PetitionsHandler.cpp \ +$(srcdir)/Player.cpp \ +$(srcdir)/Player.h \ +$(srcdir)/PlayerDump.cpp \ +$(srcdir)/PlayerDump.h \ +$(srcdir)/PointMovementGenerator.cpp \ +$(srcdir)/PointMovementGenerator.h \ +$(srcdir)/QueryHandler.cpp \ +$(srcdir)/QuestDef.cpp \ +$(srcdir)/QuestDef.h \ +$(srcdir)/QuestHandler.cpp \ +$(srcdir)/RandomMovementGenerator.cpp \ +$(srcdir)/RandomMovementGenerator.h \ +$(srcdir)/ReactorAI.cpp \ +$(srcdir)/ReactorAI.h \ +$(srcdir)/ScriptCalls.cpp \ +$(srcdir)/ScriptCalls.h \ +$(srcdir)/SharedDefines.h \ +$(srcdir)/SkillHandler.cpp \ +$(srcdir)/SpellAuraDefines.h \ +$(srcdir)/SpellAuras.cpp \ +$(srcdir)/SpellAuras.h \ +$(srcdir)/Spell.cpp \ +$(srcdir)/SpellEffects.cpp \ +$(srcdir)/Spell.h \ +$(srcdir)/SkillDiscovery.cpp \ +$(srcdir)/SkillDiscovery.h \ +$(srcdir)/SkillExtraItems.cpp \ +$(srcdir)/SkillExtraItems.h \ +$(srcdir)/SpellHandler.cpp \ +$(srcdir)/SocialMgr.cpp \ +$(srcdir)/SocialMgr.h \ +$(srcdir)/SpellMgr.cpp \ +$(srcdir)/SpellMgr.h \ +$(srcdir)/StatSystem.cpp \ +$(srcdir)/TargetedMovementGenerator.cpp \ +$(srcdir)/TargetedMovementGenerator.h \ +$(srcdir)/TaxiHandler.cpp \ +$(srcdir)/TemporarySummon.cpp \ +$(srcdir)/TemporarySummon.h \ +$(srcdir)/tools.cpp \ +$(srcdir)/Tools.h \ +$(srcdir)/TotemAI.cpp \ +$(srcdir)/TotemAI.h \ +$(srcdir)/Totem.cpp \ +$(srcdir)/Totem.h \ +$(srcdir)/TradeHandler.cpp \ +$(srcdir)/Transports.cpp \ +$(srcdir)/Transports.h \ +$(srcdir)/ThreatManager.cpp \ +$(srcdir)/ThreatManager.h \ +$(srcdir)/Traveller.h \ +$(srcdir)/Unit.cpp \ +$(srcdir)/Unit.h \ +$(srcdir)/UnitEvents.h \ +$(srcdir)/UpdateData.cpp \ +$(srcdir)/UpdateData.h \ +$(srcdir)/UpdateFields.h \ +$(srcdir)/UpdateMask.h \ +$(srcdir)/VoiceChatHandler.cpp \ +$(srcdir)/WaypointManager.cpp \ +$(srcdir)/WaypointManager.h \ +$(srcdir)/WaypointMovementGenerator.cpp \ +$(srcdir)/WaypointMovementGenerator.h \ +$(srcdir)/Weather.cpp \ +$(srcdir)/Weather.h \ +$(srcdir)/World.cpp \ +$(srcdir)/World.h \ +$(srcdir)/WorldLog.cpp \ +$(srcdir)/WorldLog.h \ +$(srcdir)/WorldSession.cpp \ +$(srcdir)/WorldSession.h \ +$(srcdir)/WorldSocket.cpp \ +$(srcdir)/WorldSocket.h \ +$(srcdir)/WorldSocketMgr.cpp \ +$(srcdir)/WorldSocketMgr.h \ +$(srcdir)/FollowerReference.cpp \ +$(srcdir)/FollowerReference.h \ +$(srcdir)/FollowerRefManager.h \ +$(srcdir)/GroupReference.cpp \ +$(srcdir)/GroupReference.h \ +$(srcdir)/GroupRefManager.h + +## Additional files to include when running 'make dist' +# Nothing yet. diff --git a/src/game/Map.cpp b/src/game/Map.cpp new file mode 100644 index 00000000000..df03bb96842 --- /dev/null +++ b/src/game/Map.cpp @@ -0,0 +1,1795 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Player.h" +#include "GridNotifiers.h" +#include "WorldSession.h" +#include "Log.h" +#include "GridStates.h" +#include "CellImpl.h" +#include "InstanceData.h" +#include "Map.h" +#include "GridNotifiersImpl.h" +#include "Config/ConfigEnv.h" +#include "Transports.h" +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "World.h" +#include "ScriptCalls.h" +#include "Group.h" + +#include "MapManager.h" +#include "MapInstanced.h" +#include "InstanceSaveMgr.h" +#include "VMapFactory.h" + +#define DEFAULT_GRID_EXPIRY 300 +#define MAX_GRID_LOAD_TIME 50 + +// magic *.map header +const char MAP_MAGIC[] = "MAP_2.00"; + +GridState* si_GridStates[MAX_GRID_STATE]; + +Map::~Map() +{ + UnloadAll(true); +} + +bool Map::ExistMap(uint32 mapid,int x,int y) +{ + int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1; + char* tmp = new char[len]; + snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y); + + FILE *pf=fopen(tmp,"rb"); + + if(!pf) + { + sLog.outError("Check existing of map file '%s': not exist!",tmp); + delete[] tmp; + return false; + } + + char magic[8]; + fread(magic,1,8,pf); + if(strncmp(MAP_MAGIC,magic,8)) + { + sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp); + delete [] tmp; + fclose(pf); //close file before return + return false; + } + + delete [] tmp; + fclose(pf); + + return true; +} + +bool Map::ExistVMap(uint32 mapid,int x,int y) +{ + if(VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager()) + { + if(vmgr->isMapLoadingEnabled()) + { + // x and y are swaped !! => fixed now + bool exists = vmgr->existsMap((sWorld.GetDataPath()+ "vmaps").c_str(), mapid, x,y); + if(!exists) + { + std::string name = vmgr->getDirFileName(mapid,x,y); + sLog.outError("VMap file '%s' is missing or point to wrong version vmap file, redo vmaps with latest vmap_assembler.exe program", (sWorld.GetDataPath()+"vmaps/"+name).c_str()); + return false; + } + } + } + + return true; +} + +void Map::LoadVMap(int x,int y) +{ + // x and y are swaped !! + int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapManager()->loadMap((sWorld.GetDataPath()+ "vmaps").c_str(), GetId(), x,y); + switch(vmapLoadResult) + { + case VMAP::VMAP_LOAD_RESULT_OK: + sLog.outDetail("VMAP loaded name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y); + break; + case VMAP::VMAP_LOAD_RESULT_ERROR: + sLog.outDetail("Could not load VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y); + break; + case VMAP::VMAP_LOAD_RESULT_IGNORED: + DEBUG_LOG("Ignored VMAP name:%s, id:%d, x:%d, y:%d (vmap rep.: x:%d, y:%d)", GetMapName(), GetId(), x,y, x,y); + break; + } +} + +void Map::LoadMap(uint32 mapid, uint32 instanceid, int x,int y) +{ + if( instanceid != 0 ) + { + if(GridMaps[x][y]) + return; + + Map* baseMap = const_cast(MapManager::Instance().GetBaseMap(mapid)); + + // load gridmap for base map + if (!baseMap->GridMaps[x][y]) + baseMap->EnsureGridCreated(GridPair(63-x,63-y)); + +//+++ if (!baseMap->GridMaps[x][y]) don't check for GridMaps[gx][gy], we need the management for vmaps +// return; + + ((MapInstanced*)(baseMap))->AddGridMapReference(GridPair(x,y)); + baseMap->SetUnloadFlag(GridPair(63-x,63-y), false); + GridMaps[x][y] = baseMap->GridMaps[x][y]; + return; + } + + //map already load, delete it before reloading (Is it neccessary? Do we really need the abilty the reload maps during runtime?) + if(GridMaps[x][y]) + { + sLog.outDetail("Unloading already loaded map %u before reloading.",mapid); + delete (GridMaps[x][y]); + GridMaps[x][y]=NULL; + } + + // map file name + char *tmp=NULL; + // Pihhan: dataPath length + "maps/" + 3+2+2+ ".map" length may be > 32 ! + int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1; + tmp = new char[len]; + snprintf(tmp, len, (char *)(sWorld.GetDataPath()+"maps/%03u%02u%02u.map").c_str(),mapid,x,y); + sLog.outDetail("Loading map %s",tmp); + // loading data + FILE *pf=fopen(tmp,"rb"); + if(!pf) + { + delete [] tmp; + return; + } + + char magic[8]; + fread(magic,1,8,pf); + if(strncmp(MAP_MAGIC,magic,8)) + { + sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp); + delete [] tmp; + fclose(pf); //close file before return + return; + } + delete [] tmp; + + GridMap * buf= new GridMap; + fread(buf,1,sizeof(GridMap),pf); + fclose(pf); + + GridMaps[x][y] = buf; +} + +void Map::LoadMapAndVMap(uint32 mapid, uint32 instanceid, int x,int y) +{ + LoadMap(mapid,instanceid,x,y); + if(instanceid == 0) + LoadVMap(x, y); // Only load the data for the base map +} + +void Map::InitStateMachine() +{ + si_GridStates[GRID_STATE_INVALID] = new InvalidState; + si_GridStates[GRID_STATE_ACTIVE] = new ActiveState; + si_GridStates[GRID_STATE_IDLE] = new IdleState; + si_GridStates[GRID_STATE_REMOVAL] = new RemovalState; +} + +void Map::DeleteStateMachine() +{ + delete si_GridStates[GRID_STATE_INVALID]; + delete si_GridStates[GRID_STATE_ACTIVE]; + delete si_GridStates[GRID_STATE_IDLE]; + delete si_GridStates[GRID_STATE_REMOVAL]; +} + +Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) + : i_id(id), i_gridExpiry(expiry), i_mapEntry (sMapStore.LookupEntry(id)), + i_InstanceId(InstanceId), i_spawnMode(SpawnMode), m_unloadTimer(0) +{ + for(unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx) + { + for(unsigned int j=0; j < MAX_NUMBER_OF_GRIDS; ++j) + { + //z code + GridMaps[idx][j] =NULL; + setNGrid(NULL, idx, j); + } + } +} + +// Template specialization of utility methods +template +void Map::AddToGrid(T* obj, NGridType *grid, Cell const& cell) +{ + (*grid)(cell.CellX(), cell.CellY()).template AddGridObject(obj, obj->GetGUID()); +} + +template<> +void Map::AddToGrid(Player* obj, NGridType *grid, Cell const& cell) +{ + (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID()); +} + +template<> +void Map::AddToGrid(Corpse *obj, NGridType *grid, Cell const& cell) +{ + // add to world object registry in grid + if(obj->GetType()!=CORPSE_BONES) + { + (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID()); + } + // add to grid object store + else + { + (*grid)(cell.CellX(), cell.CellY()).AddGridObject(obj, obj->GetGUID()); + } +} + +template<> +void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell) +{ + // add to world object registry in grid + if(obj->isPet()) + { + (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID()); + obj->SetCurrentCell(cell); + } + // add to grid object store + else + { + (*grid)(cell.CellX(), cell.CellY()).AddGridObject(obj, obj->GetGUID()); + obj->SetCurrentCell(cell); + } +} + +template +void Map::RemoveFromGrid(T* obj, NGridType *grid, Cell const& cell) +{ + (*grid)(cell.CellX(), cell.CellY()).template RemoveGridObject(obj, obj->GetGUID()); +} + +template<> +void Map::RemoveFromGrid(Player* obj, NGridType *grid, Cell const& cell) +{ + (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID()); +} + +template<> +void Map::RemoveFromGrid(Corpse *obj, NGridType *grid, Cell const& cell) +{ + // remove from world object registry in grid + if(obj->GetType()!=CORPSE_BONES) + { + (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID()); + } + // remove from grid object store + else + { + (*grid)(cell.CellX(), cell.CellY()).RemoveGridObject(obj, obj->GetGUID()); + } +} + +template<> +void Map::RemoveFromGrid(Creature* obj, NGridType *grid, Cell const& cell) +{ + // remove from world object registry in grid + if(obj->isPet()) + { + (*grid)(cell.CellX(), cell.CellY()).RemoveWorldObject(obj, obj->GetGUID()); + } + // remove from grid object store + else + { + (*grid)(cell.CellX(), cell.CellY()).RemoveGridObject(obj, obj->GetGUID()); + } +} + +template +void Map::DeleteFromWorld(T* obj) +{ + // Note: In case resurrectable corpse and pet its removed from gloabal lists in own destructors + delete obj; +} + +template +void Map::AddNotifier(T* , Cell const& , CellPair const& ) +{ +} + +template<> +void Map::AddNotifier(Player* obj, Cell const& cell, CellPair const& cellpair) +{ + PlayerRelocationNotify(obj,cell,cellpair); +} + +template<> +void Map::AddNotifier(Creature* obj, Cell const& cell, CellPair const& cellpair) +{ + CreatureRelocationNotify(obj,cell,cellpair); +} + +void +Map::EnsureGridCreated(const GridPair &p) +{ + if(!getNGrid(p.x_coord, p.y_coord)) + { + Guard guard(*this); + if(!getNGrid(p.x_coord, p.y_coord)) + { + setNGrid(new NGridType(p.x_coord*MAX_NUMBER_OF_GRIDS + p.y_coord, p.x_coord, p.y_coord, i_gridExpiry, sWorld.getConfig(CONFIG_GRID_UNLOAD)), + p.x_coord, p.y_coord); + + // build a linkage between this map and NGridType + buildNGridLinkage(getNGrid(p.x_coord, p.y_coord)); + + getNGrid(p.x_coord, p.y_coord)->SetGridState(GRID_STATE_IDLE); + + //z coord + int gx=63-p.x_coord; + int gy=63-p.y_coord; + + if(!GridMaps[gx][gy]) + Map::LoadMapAndVMap(i_id,i_InstanceId,gx,gy); + } + } +} + +void +Map::EnsureGridLoadedForPlayer(const Cell &cell, Player *player, bool add_player) +{ + EnsureGridCreated(GridPair(cell.GridX(), cell.GridY())); + NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); + + assert(grid != NULL); + if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) ) + { + if( player != NULL ) + { + player->SendDelayResponse(MAX_GRID_LOAD_TIME); + DEBUG_LOG("Player %s enter cell[%u,%u] triggers of loading grid[%u,%u] on map %u", player->GetName(), cell.CellX(), cell.CellY(), cell.GridX(), cell.GridY(), i_id); + } + else + { + DEBUG_LOG("Player nearby triggers of loading grid [%u,%u] on map %u", cell.GridX(), cell.GridY(), i_id); + } + + ObjectGridLoader loader(*grid, this, cell); + loader.LoadN(); + setGridObjectDataLoaded(true, cell.GridX(), cell.GridY()); + + // Add resurrectable corpses to world object list in grid + ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this); + + ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f); + grid->SetGridState(GRID_STATE_ACTIVE); + + if( add_player && player != NULL ) + (*grid)(cell.CellX(), cell.CellY()).AddWorldObject(player, player->GetGUID()); + } + else if( player && add_player ) + AddToGrid(player,grid,cell); +} + +void +Map::LoadGrid(const Cell& cell, bool no_unload) +{ + EnsureGridCreated(GridPair(cell.GridX(), cell.GridY())); + NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); + + assert(grid != NULL); + if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) ) + { + ObjectGridLoader loader(*grid, this, cell); + loader.LoadN(); + + // Add resurrectable corpses to world object list in grid + ObjectAccessor::Instance().AddCorpsesToGrid(GridPair(cell.GridX(),cell.GridY()),(*grid)(cell.CellX(), cell.CellY()), this); + + setGridObjectDataLoaded(true,cell.GridX(), cell.GridY()); + if(no_unload) + getNGrid(cell.GridX(), cell.GridY())->setUnloadFlag(false); + } + LoadVMap(63-cell.GridX(),63-cell.GridY()); +} + +bool Map::Add(Player *player) +{ + player->SetInstanceId(this->GetInstanceId()); + + // update player state for other player and visa-versa + CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); + Cell cell(p); + EnsureGridLoadedForPlayer(cell, player, true); + player->AddToWorld(); + + SendInitSelf(player); + SendInitTransports(player); + + UpdatePlayerVisibility(player,cell,p); + UpdateObjectsVisibilityFor(player,cell,p); + + AddNotifier(player,cell,p); + return true; +} + +template +void +Map::Add(T *obj) +{ + CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); + + assert(obj); + + if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP ) + { + sLog.outError("Map::Add: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord); + return; + } + + Cell cell(p); + EnsureGridCreated(GridPair(cell.GridX(), cell.GridY())); + NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); + assert( grid != NULL ); + + AddToGrid(obj,grid,cell); + obj->AddToWorld(); + + DEBUG_LOG("Object %u enters grid[%u,%u]", GUID_LOPART(obj->GetGUID()), cell.GridX(), cell.GridY()); + + UpdateObjectVisibility(obj,cell,p); + + AddNotifier(obj,cell,p); +} + +void Map::MessageBroadcast(Player *player, WorldPacket *msg, bool to_self) +{ + CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); + + if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP ) + { + sLog.outError("Map::MessageBroadcast: Player (GUID: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), p.x_coord, p.y_coord); + return; + } + + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) ) + return; + + MaNGOS::MessageDeliverer post_man(*player, msg, to_self); + TypeContainerVisitor message(post_man); + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, message, *this); +} + +void Map::MessageBroadcast(WorldObject *obj, WorldPacket *msg) +{ + CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); + + if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP ) + { + sLog.outError("Map::MessageBroadcast: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord); + return; + } + + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) ) + return; + + MaNGOS::ObjectMessageDeliverer post_man(msg); + TypeContainerVisitor message(post_man); + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, message, *this); +} + +void Map::MessageDistBroadcast(Player *player, WorldPacket *msg, float dist, bool to_self, bool own_team_only) +{ + CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); + + if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP ) + { + sLog.outError("Map::MessageBroadcast: Player (GUID: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), p.x_coord, p.y_coord); + return; + } + + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) ) + return; + + MaNGOS::MessageDistDeliverer post_man(*player, msg, dist, to_self, own_team_only); + TypeContainerVisitor message(post_man); + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, message, *this); +} + +void Map::MessageDistBroadcast(WorldObject *obj, WorldPacket *msg, float dist) +{ + CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); + + if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP ) + { + sLog.outError("Map::MessageBroadcast: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord); + return; + } + + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) ) + return; + + MaNGOS::ObjectMessageDistDeliverer post_man(*obj, msg,dist); + TypeContainerVisitor message(post_man); + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, message, *this); +} + +bool Map::loaded(const GridPair &p) const +{ + return ( getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord) ); +} + +void Map::Update(const uint32 &t_diff) +{ + // Don't unload grids if it's battleground, since we may have manually added GOs,creatures, those doesn't load from DB at grid re-load ! + // This isn't really bother us, since as soon as we have instanced BG-s, the whole map unloads as the BG gets ended + if (IsBattleGroundOrArena()) + return; + + for (GridRefManager::iterator i = GridRefManager::begin(); i != GridRefManager::end(); ) + { + NGridType *grid = i->getSource(); + GridInfo *info = i->getSource()->getGridInfoRef(); + ++i; // The update might delete the map and we need the next map before the iterator gets invalid + assert(grid->GetGridState() >= 0 && grid->GetGridState() < MAX_GRID_STATE); + si_GridStates[grid->GetGridState()]->Update(*this, *grid, *info, grid->getX(), grid->getY(), t_diff); + } +} + +void InstanceMap::Update(const uint32& t_diff) +{ + Map::Update(t_diff); + + if(i_data) + i_data->Update(t_diff); +} + + +void Map::Remove(Player *player, bool remove) +{ + CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); + if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP) + { + // invalid coordinates + player->RemoveFromWorld(); + + if( remove ) + DeleteFromWorld(player); + + return; + } + + Cell cell(p); + + if( !getNGrid(cell.data.Part.grid_x, cell.data.Part.grid_y) ) + { + sLog.outError("Map::Remove() i_grids was NULL x:%d, y:%d",cell.data.Part.grid_x,cell.data.Part.grid_y); + return; + } + + DEBUG_LOG("Remove player %s from grid[%u,%u]", player->GetName(), cell.GridX(), cell.GridY()); + NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); + assert(grid != NULL); + + player->RemoveFromWorld(); + RemoveFromGrid(player,grid,cell); + + SendRemoveTransports(player); + + UpdateObjectsVisibilityFor(player,cell,p); + + if( remove ) + DeleteFromWorld(player); +} + +bool Map::RemoveBones(uint64 guid, float x, float y) +{ + if (IsRemovalGrid(x, y)) + { + Corpse * corpse = ObjectAccessor::Instance().GetObjectInWorld(GetId(), x, y, guid, (Corpse*)NULL); + if(corpse && corpse->GetTypeId() == TYPEID_CORPSE && corpse->GetType() == CORPSE_BONES) + corpse->DeleteBonesFromWorld(); + else + return false; + } + return true; +} + +template +void +Map::Remove(T *obj, bool remove) +{ + CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); + if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP ) + { + sLog.outError("Map::Remove: Object " I64FMTD " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord); + return; + } + + Cell cell(p); + if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) ) + return; + + DEBUG_LOG("Remove object " I64FMTD " from grid[%u,%u]", obj->GetGUID(), cell.data.Part.grid_x, cell.data.Part.grid_y); + NGridType *grid = getNGrid(cell.GridX(), cell.GridY()); + assert( grid != NULL ); + + obj->RemoveFromWorld(); + RemoveFromGrid(obj,grid,cell); + + UpdateObjectVisibility(obj,cell,p); + + if( remove ) + { + // if option set then object already saved at this moment + if(!sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY)) + obj->SaveRespawnTime(); + DeleteFromWorld(obj); + } +} + +void +Map::PlayerRelocation(Player *player, float x, float y, float z, float orientation) +{ + assert(player); + + CellPair old_val = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); + CellPair new_val = MaNGOS::ComputeCellPair(x, y); + + Cell old_cell(old_val); + Cell new_cell(new_val); + new_cell |= old_cell; + bool same_cell = (new_cell == old_cell); + + player->Relocate(x, y, z, orientation); + + if( old_cell.DiffGrid(new_cell) || old_cell.DiffCell(new_cell) ) + { + DEBUG_LOG("Player %s relocation grid[%u,%u]cell[%u,%u]->grid[%u,%u]cell[%u,%u]", player->GetName(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + + // update player position for group at taxi flight + if(player->GetGroup() && player->isInFlight()) + player->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION); + + NGridType* oldGrid = getNGrid(old_cell.GridX(), old_cell.GridY()); + RemoveFromGrid(player, oldGrid,old_cell); + if( !old_cell.DiffGrid(new_cell) ) + AddToGrid(player, oldGrid,new_cell); + + if( old_cell.DiffGrid(new_cell) ) + EnsureGridLoadedForPlayer(new_cell, player, true); + } + + // if move then update what player see and who seen + UpdatePlayerVisibility(player,new_cell,new_val); + UpdateObjectsVisibilityFor(player,new_cell,new_val); + PlayerRelocationNotify(player,new_cell,new_val); + NGridType* newGrid = getNGrid(new_cell.GridX(), new_cell.GridY()); + if( !same_cell && newGrid->GetGridState()!= GRID_STATE_ACTIVE ) + { + ResetGridExpiry(*newGrid, 0.1f); + newGrid->SetGridState(GRID_STATE_ACTIVE); + } +} + +void +Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang) +{ + assert(CheckGridIntegrity(creature,false)); + + Cell old_cell = creature->GetCurrentCell(); + + CellPair new_val = MaNGOS::ComputeCellPair(x, y); + Cell new_cell(new_val); + + // delay creature move for grid/cell to grid/cell moves + if( old_cell.DiffCell(new_cell) || old_cell.DiffGrid(new_cell) ) + { + #ifdef MANGOS_DEBUG + if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0) + sLog.outDebug("Creature (GUID: %u Entry: %u) added to moving list from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", creature->GetGUIDLow(), creature->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + #endif + AddCreatureToMoveList(creature,x,y,z,ang); + // in diffcell/diffgrid case notifiers called at finishing move creature in Map::MoveAllCreaturesInMoveList + } + else + { + creature->Relocate(x, y, z, ang); + CreatureRelocationNotify(creature,new_cell,new_val); + } + assert(CheckGridIntegrity(creature,true)); +} + +void Map::AddCreatureToMoveList(Creature *c, float x, float y, float z, float ang) +{ + if(!c) + return; + + i_creaturesToMove[c] = CreatureMover(x,y,z,ang); +} + +void Map::MoveAllCreaturesInMoveList() +{ + while(!i_creaturesToMove.empty()) + { + // get data and remove element; + CreatureMoveList::iterator iter = i_creaturesToMove.begin(); + Creature* c = iter->first; + CreatureMover cm = iter->second; + i_creaturesToMove.erase(iter); + + // calculate cells + CellPair new_val = MaNGOS::ComputeCellPair(cm.x, cm.y); + Cell new_cell(new_val); + + // do move or do move to respawn or remove creature if previous all fail + if(CreatureCellRelocation(c,new_cell)) + { + // update pos + c->Relocate(cm.x, cm.y, cm.z, cm.ang); + CreatureRelocationNotify(c,new_cell,new_cell.cellPair()); + } + else + { + // if creature can't be move in new cell/grid (not loaded) move it to repawn cell/grid + // creature coordinates will be updated and notifiers send + if(!CreatureRespawnRelocation(c)) + { + // ... or unload (if respawn grid also not loaded) + #ifdef MANGOS_DEBUG + if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0) + sLog.outDebug("Creature (GUID: %u Entry: %u ) can't be move to unloaded respawn grid.",c->GetGUIDLow(),c->GetEntry()); + #endif + c->CleanupsBeforeDelete(); + AddObjectToRemoveList(c); + } + } + } +} + +bool Map::CreatureCellRelocation(Creature *c, Cell new_cell) +{ + Cell const& old_cell = c->GetCurrentCell(); + if(!old_cell.DiffGrid(new_cell) ) // in same grid + { + // if in same cell then none do + if(old_cell.DiffCell(new_cell)) + { + #ifdef MANGOS_DEBUG + if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0) + sLog.outDebug("Creature (GUID: %u Entry: %u) moved in grid[%u,%u] from cell[%u,%u] to cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.CellX(), new_cell.CellY()); + #endif + + if( !old_cell.DiffGrid(new_cell) ) + { + RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell); + AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell); + c->SetCurrentCell(new_cell); + } + } + else + { + #ifdef MANGOS_DEBUG + if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0) + sLog.outDebug("Creature (GUID: %u Entry: %u) move in same grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY()); + #endif + } + } + else // in diff. grids + if(loaded(GridPair(new_cell.GridX(), new_cell.GridY()))) + { + #ifdef MANGOS_DEBUG + if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0) + sLog.outDebug("Creature (GUID: %u Entry: %u) moved from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + #endif + + RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell); + { + EnsureGridCreated(GridPair(new_cell.GridX(), new_cell.GridY())); + AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell); + } + } + else + { + #ifdef MANGOS_DEBUG + if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0) + sLog.outDebug("Creature (GUID: %u Entry: %u) attempt move from grid[%u,%u]cell[%u,%u] to unloaded grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY()); + #endif + return false; + } + + return true; +} + +bool Map::CreatureRespawnRelocation(Creature *c) +{ + float resp_x, resp_y, resp_z, resp_o; + c->GetRespawnCoord(resp_x, resp_y, resp_z, &resp_o); + + CellPair resp_val = MaNGOS::ComputeCellPair(resp_x, resp_y); + Cell resp_cell(resp_val); + + c->CombatStop(); + c->GetMotionMaster()->Clear(); + + #ifdef MANGOS_DEBUG + if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0) + sLog.outDebug("Creature (GUID: %u Entry: %u) will moved from grid[%u,%u]cell[%u,%u] to respawn grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), c->GetCurrentCell().GridX(), c->GetCurrentCell().GridY(), c->GetCurrentCell().CellX(), c->GetCurrentCell().CellY(), resp_cell.GridX(), resp_cell.GridY(), resp_cell.CellX(), resp_cell.CellY()); + #endif + + // teleport it to respawn point (like normal respawn if player see) + if(CreatureCellRelocation(c,resp_cell)) + { + c->Relocate(resp_x, resp_y, resp_z, resp_o); + c->GetMotionMaster()->Initialize(); // prevent possible problems with default move generators + CreatureRelocationNotify(c,resp_cell,resp_cell.cellPair()); + return true; + } + else + return false; +} + +bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool pForce) +{ + NGridType *grid = getNGrid(x, y); + assert( grid != NULL); + + { + if(!pForce && ObjectAccessor::Instance().PlayersNearGrid(x, y, i_id, i_InstanceId) ) + return false; + + DEBUG_LOG("Unloading grid[%u,%u] for map %u", x,y, i_id); + ObjectGridUnloader unloader(*grid); + + // Finish creature moves, remove and delete all creatures with delayed remove before moving to respawn grids + // Must know real mob position before move + DoDelayedMovesAndRemoves(); + + // move creatures to respawn grids if this is diff.grid or to remove list + unloader.MoveToRespawnN(); + + // Finish creature moves, remove and delete all creatures with delayed remove before unload + DoDelayedMovesAndRemoves(); + + unloader.UnloadN(); + delete getNGrid(x, y); + setNGrid(NULL, x, y); + } + int gx=63-x; + int gy=63-y; + + // delete grid map, but don't delete if it is from parent map (and thus only reference) + //+++if (GridMaps[gx][gy]) don't check for GridMaps[gx][gy], we might have to unload vmaps + { + if (i_InstanceId == 0) + { + if(GridMaps[gx][gy]) delete (GridMaps[gx][gy]); + // x and y are swaped + VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(GetId(), gy, gx); + } + else + ((MapInstanced*)(MapManager::Instance().GetBaseMap(i_id)))->RemoveGridMapReference(GridPair(gx, gy)); + GridMaps[gx][gy] = NULL; + } + DEBUG_LOG("Unloading grid[%u,%u] for map %u finished", x,y, i_id); + return true; +} + +void Map::UnloadAll(bool pForce) +{ + // clear all delayed moves, useless anyway do this moves before map unload. + i_creaturesToMove.clear(); + + for (GridRefManager::iterator i = GridRefManager::begin(); i != GridRefManager::end(); ) + { + NGridType &grid(*i->getSource()); + ++i; + UnloadGrid(grid.getX(), grid.getY(), pForce); // deletes the grid and removes it from the GridRefManager + } +} + +float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const +{ + GridPair p = MaNGOS::ComputeGridPair(x, y); + + // half opt method + int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x + int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y + + float lx=MAP_RESOLUTION*(32 -x/SIZE_OF_GRIDS - gx); + float ly=MAP_RESOLUTION*(32 -y/SIZE_OF_GRIDS - gy); + + // ensure GridMap is loaded + const_cast(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); + + // find raw .map surface under Z coordinates + float mapHeight; + if(GridMap* gmap = GridMaps[gx][gy]) + { + int lx_int = (int)lx; + int ly_int = (int)ly; + + float zi[4]; + // Probe 4 nearest points (except border cases) + zi[0] = gmap->Z[lx_int][ly_int]; + zi[1] = lx < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int] : zi[0]; + zi[2] = ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int][ly_int+1] : zi[0]; + zi[3] = lx < MAP_RESOLUTION-1 && ly < MAP_RESOLUTION-1 ? gmap->Z[lx_int+1][ly_int+1] : zi[0]; + // Recalculate them like if their x,y positions were in the range 0,1 + float b[4]; + b[0] = zi[0]; + b[1] = zi[1]-zi[0]; + b[2] = zi[2]-zi[0]; + b[3] = zi[0]-zi[1]-zi[2]+zi[3]; + // Normalize the dx and dy to be in range 0..1 + float fact_x = lx - lx_int; + float fact_y = ly - ly_int; + // Use the simplified bilinear equation, as described in [url="http://en.wikipedia.org/wiki/Bilinear_interpolation"]http://en.wikipedia.org/wiki/Bilinear_interpolation[/url] + float _mapheight = b[0] + (b[1]*fact_x) + (b[2]*fact_y) + (b[3]*fact_x*fact_y); + + // look from a bit higher pos to find the floor, ignore under surface case + if(z + 2.0f > _mapheight) + mapHeight = _mapheight; + else + mapHeight = VMAP_INVALID_HEIGHT_VALUE; + } + else + mapHeight = VMAP_INVALID_HEIGHT_VALUE; + + float vmapHeight; + if(pUseVmaps) + { + VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); + if(vmgr->isHeightCalcEnabled()) + { + // look from a bit higher pos to find the floor + vmapHeight = vmgr->getHeight(GetId(), x, y, z + 2.0f); + } + else + vmapHeight = VMAP_INVALID_HEIGHT_VALUE; + } + else + vmapHeight = VMAP_INVALID_HEIGHT_VALUE; + + // mapHeight set for any above raw ground Z or <= INVALID_HEIGHT + // vmapheight set for any under Z value or <= INVALID_HEIGHT + + if( vmapHeight > INVALID_HEIGHT ) + { + if( mapHeight > INVALID_HEIGHT ) + { + // we have mapheight and vmapheight and must select more appropriate + + // we are already under the surface or vmap height above map heigt + // or if the distance of the vmap height is less the land height distance + if( z < mapHeight || vmapHeight > mapHeight || fabs(mapHeight-z) > fabs(vmapHeight-z) ) + return vmapHeight; + else + return mapHeight; // better use .map surface height + + } + else + return vmapHeight; // we have only vmapHeight (if have) + } + else + { + if(!pUseVmaps) + return mapHeight; // explicitly use map data (if have) + else if(mapHeight > INVALID_HEIGHT && (z < mapHeight + 2 || z == MAX_HEIGHT)) + return mapHeight; // explicitly use map data if original z < mapHeight but map found (z+2 > mapHeight) + else + return VMAP_INVALID_HEIGHT_VALUE; // we not have any height + } +} + +uint16 Map::GetAreaFlag(float x, float y ) const +{ + //local x,y coords + float lx,ly; + int gx,gy; + GridPair p = MaNGOS::ComputeGridPair(x, y); + + // half opt method + gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x + gy=(int)(32-y/SIZE_OF_GRIDS); //grid y + + lx=16*(32 -x/SIZE_OF_GRIDS - gx); + ly=16*(32 -y/SIZE_OF_GRIDS - gy); + //DEBUG_LOG("my %d %d si %d %d",gx,gy,p.x_coord,p.y_coord); + + // ensure GridMap is loaded + const_cast(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); + + if(GridMaps[gx][gy]) + return GridMaps[gx][gy]->area_flag[(int)(lx)][(int)(ly)]; + // this used while not all *.map files generated (instances) + else + return GetAreaFlagByMapId(i_id); +} + +uint8 Map::GetTerrainType(float x, float y ) const +{ + //local x,y coords + float lx,ly; + int gx,gy; + + // half opt method + gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x + gy=(int)(32-y/SIZE_OF_GRIDS); //grid y + + lx=16*(32 -x/SIZE_OF_GRIDS - gx); + ly=16*(32 -y/SIZE_OF_GRIDS - gy); + + // ensure GridMap is loaded + const_cast(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); + + if(GridMaps[gx][gy]) + return GridMaps[gx][gy]->terrain_type[(int)(lx)][(int)(ly)]; + else + return 0; + +} + +float Map::GetWaterLevel(float x, float y ) const +{ + //local x,y coords + float lx,ly; + int gx,gy; + + // half opt method + gx=(int)(32-x/SIZE_OF_GRIDS) ; //grid x + gy=(int)(32-y/SIZE_OF_GRIDS); //grid y + + lx=128*(32 -x/SIZE_OF_GRIDS - gx); + ly=128*(32 -y/SIZE_OF_GRIDS - gy); + + // ensure GridMap is loaded + const_cast(this)->EnsureGridCreated(GridPair(63-gx,63-gy)); + + if(GridMaps[gx][gy]) + return GridMaps[gx][gy]->liquid_level[(int)(lx)][(int)(ly)]; + else + return 0; +} + +uint32 Map::GetAreaId(uint16 areaflag,uint32 map_id) +{ + AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id); + + if (entry) + return entry->ID; + else + return 0; +} + +uint32 Map::GetZoneId(uint16 areaflag,uint32 map_id) +{ + AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id); + + if( entry ) + return ( entry->zone != 0 ) ? entry->zone : entry->ID; + else + return 0; +} + +bool Map::IsInWater(float x, float y, float pZ) const +{ + // This method is called too often to use vamps for that (4. parameter = false). + // The pZ pos is taken anyway for future use + float z = GetHeight(x,y,pZ,false); // use .map base surface height + + // underground or instance without vmap + if(z <= INVALID_HEIGHT) + return false; + + float water_z = GetWaterLevel(x,y); + uint8 flag = GetTerrainType(x,y); + return (z < (water_z-2)) && (flag & 0x01); +} + +bool Map::IsUnderWater(float x, float y, float z) const +{ + float water_z = GetWaterLevel(x,y); + uint8 flag = GetTerrainType(x,y); + return (z < (water_z-2)) && (flag & 0x01); +} + +bool Map::CheckGridIntegrity(Creature* c, bool moved) const +{ + Cell const& cur_cell = c->GetCurrentCell(); + + CellPair xy_val = MaNGOS::ComputeCellPair(c->GetPositionX(), c->GetPositionY()); + Cell xy_cell(xy_val); + if(xy_cell != cur_cell) + { + sLog.outError("ERROR: %s (GUID: %u) X: %f Y: %f (%s) in grid[%u,%u]cell[%u,%u] instead grid[%u,%u]cell[%u,%u]", + (c->GetTypeId()==TYPEID_PLAYER ? "Player" : "Creature"),c->GetGUIDLow(), + c->GetPositionX(),c->GetPositionY(),(moved ? "final" : "original"), + cur_cell.GridX(), cur_cell.GridY(), cur_cell.CellX(), cur_cell.CellY(), + xy_cell.GridX(), xy_cell.GridY(), xy_cell.CellX(), xy_cell.CellY()); + return true; // not crash at error, just output error in debug mode + } + + return true; +} + +const char* Map::GetMapName() const +{ + return i_mapEntry ? i_mapEntry->name[sWorld.GetDefaultDbcLocale()] : "UNNAMEDMAP\x0"; +} + +void Map::UpdateObjectVisibility( WorldObject* obj, Cell cell, CellPair cellpair) +{ + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + MaNGOS::VisibleChangesNotifier notifier(*obj); + TypeContainerVisitor player_notifier(notifier); + CellLock cell_lock(cell, cellpair); + cell_lock->Visit(cell_lock, player_notifier, *this); +} + +void Map::UpdatePlayerVisibility( Player* player, Cell cell, CellPair cellpair ) +{ + cell.data.Part.reserved = ALL_DISTRICT; + + MaNGOS::PlayerNotifier pl_notifier(*player); + TypeContainerVisitor player_notifier(pl_notifier); + + CellLock cell_lock(cell, cellpair); + cell_lock->Visit(cell_lock, player_notifier, *this); +} + +void Map::UpdateObjectsVisibilityFor( Player* player, Cell cell, CellPair cellpair ) +{ + MaNGOS::VisibleNotifier notifier(*player); + + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + TypeContainerVisitor world_notifier(notifier); + TypeContainerVisitor grid_notifier(notifier); + CellLock cell_lock(cell, cellpair); + cell_lock->Visit(cell_lock, world_notifier, *this); + cell_lock->Visit(cell_lock, grid_notifier, *this); + + // send data + notifier.Notify(); +} + +void Map::PlayerRelocationNotify( Player* player, Cell cell, CellPair cellpair ) +{ + CellLock cell_lock(cell, cellpair); + MaNGOS::PlayerRelocationNotifier relocationNotifier(*player); + cell.data.Part.reserved = ALL_DISTRICT; + + TypeContainerVisitor p2grid_relocation(relocationNotifier); + TypeContainerVisitor p2world_relocation(relocationNotifier); + + cell_lock->Visit(cell_lock, p2grid_relocation, *this); + cell_lock->Visit(cell_lock, p2world_relocation, *this); +} + +void Map::CreatureRelocationNotify(Creature *creature, Cell cell, CellPair cellpair) +{ + CellLock cell_lock(cell, cellpair); + MaNGOS::CreatureRelocationNotifier relocationNotifier(*creature); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); // not trigger load unloaded grids at notifier call + + TypeContainerVisitor c2world_relocation(relocationNotifier); + TypeContainerVisitor c2grid_relocation(relocationNotifier); + + cell_lock->Visit(cell_lock, c2world_relocation, *this); + cell_lock->Visit(cell_lock, c2grid_relocation, *this); +} + +void Map::SendInitSelf( Player * player ) +{ + sLog.outDetail("Creating player data for himself %u", player->GetGUIDLow()); + + UpdateData data; + + bool hasTransport = false; + + // attach to player data current transport data + if(Transport* transport = player->GetTransport()) + { + hasTransport = true; + transport->BuildCreateUpdateBlockForPlayer(&data, player); + } + + // build data for self presence in world at own client (one time for map) + player->BuildCreateUpdateBlockForPlayer(&data, player); + + // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map + if(Transport* transport = player->GetTransport()) + { + for(Transport::PlayerSet::const_iterator itr = transport->GetPassengers().begin();itr!=transport->GetPassengers().end();++itr) + { + if(player!=(*itr) && player->HaveAtClient(*itr)) + { + hasTransport = true; + (*itr)->BuildCreateUpdateBlockForPlayer(&data, player); + } + } + } + + WorldPacket packet; + data.BuildPacket(&packet, hasTransport); + player->GetSession()->SendPacket(&packet); +} + +void Map::SendInitTransports( Player * player ) +{ + // Hack to send out transports + MapManager::TransportMap& tmap = MapManager::Instance().m_TransportsByMap; + + // no transports at map + if (tmap.find(player->GetMapId()) == tmap.end()) + return; + + UpdateData transData; + + MapManager::TransportSet& tset = tmap[player->GetMapId()]; + + bool hasTransport = false; + + for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i) + { + if((*i) != player->GetTransport()) // send data for current transport in other place + { + hasTransport = true; + (*i)->BuildCreateUpdateBlockForPlayer(&transData, player); + } + } + + WorldPacket packet; + transData.BuildPacket(&packet, hasTransport); + player->GetSession()->SendPacket(&packet); +} + +void Map::SendRemoveTransports( Player * player ) +{ + // Hack to send out transports + MapManager::TransportMap& tmap = MapManager::Instance().m_TransportsByMap; + + // no transports at map + if (tmap.find(player->GetMapId()) == tmap.end()) + return; + + UpdateData transData; + + MapManager::TransportSet& tset = tmap[player->GetMapId()]; + + // except used transport + for (MapManager::TransportSet::iterator i = tset.begin(); i != tset.end(); ++i) + if(player->GetTransport() != (*i)) + (*i)->BuildOutOfRangeUpdateBlock(&transData); + + WorldPacket packet; + transData.BuildPacket(&packet); + player->GetSession()->SendPacket(&packet); +} + +inline void Map::setNGrid(NGridType *grid, uint32 x, uint32 y) +{ + if(x >= MAX_NUMBER_OF_GRIDS || y >= MAX_NUMBER_OF_GRIDS) + { + sLog.outError("map::setNGrid() Invalid grid coordinates found: %d, %d!",x,y); + assert(false); + } + i_grids[x][y] = grid; +} + +void Map::DoDelayedMovesAndRemoves() +{ + MoveAllCreaturesInMoveList(); + RemoveAllObjectsInRemoveList(); +} + +void Map::AddObjectToRemoveList(WorldObject *obj) +{ + assert(obj->GetMapId()==GetId() && obj->GetInstanceId()==GetInstanceId()); + + i_objectsToRemove.insert(obj); + //sLog.outDebug("Object (GUID: %u TypeId: %u ) added to removing list.",obj->GetGUIDLow(),obj->GetTypeId()); +} + +void Map::RemoveAllObjectsInRemoveList() +{ + if(i_objectsToRemove.empty()) + return; + + //sLog.outDebug("Object remover 1 check."); + while(!i_objectsToRemove.empty()) + { + WorldObject* obj = *i_objectsToRemove.begin(); + i_objectsToRemove.erase(i_objectsToRemove.begin()); + + switch(obj->GetTypeId()) + { + case TYPEID_CORPSE: + { + Corpse* corpse = ObjectAccessor::Instance().GetCorpse(*obj, obj->GetGUID()); + if (!corpse) + sLog.outError("ERROR: Try delete corpse/bones %u that not in map", obj->GetGUIDLow()); + else + Remove(corpse,true); + break; + } + case TYPEID_DYNAMICOBJECT: + Remove((DynamicObject*)obj,true); + break; + case TYPEID_GAMEOBJECT: + Remove((GameObject*)obj,true); + break; + case TYPEID_UNIT: + Remove((Creature*)obj,true); + break; + default: + sLog.outError("Non-grid object (TypeId: %u) in grid object removing list, ignored.",obj->GetTypeId()); + break; + } + } + //sLog.outDebug("Object remover 2 check."); +} + +bool Map::CanUnload(const uint32 &diff) +{ + if(!m_unloadTimer) return false; + if(m_unloadTimer < diff) return true; + m_unloadTimer -= diff; + return false; +} + +template void Map::Add(Corpse *); +template void Map::Add(Creature *); +template void Map::Add(GameObject *); +template void Map::Add(DynamicObject *); + +template void Map::Remove(Corpse *,bool); +template void Map::Remove(Creature *,bool); +template void Map::Remove(GameObject *, bool); +template void Map::Remove(DynamicObject *, bool); + +/* ******* Dungeon Instance Maps ******* */ + +InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode) + : Map(id, expiry, InstanceId, SpawnMode), i_data(NULL), + m_resetAfterUnload(false), m_unloadWhenEmpty(false) +{ + // the timer is started by default, and stopped when the first player joins + // this make sure it gets unloaded if for some reason no player joins + m_unloadTimer = std::max(sWorld.getConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY); +} + +InstanceMap::~InstanceMap() +{ + if(i_data) + { + delete i_data; + i_data = NULL; + } +} + +/* + Do map specific checks to see if the player can enter +*/ +bool InstanceMap::CanEnter(Player *player) +{ + if(std::find(i_Players.begin(),i_Players.end(),player)!=i_Players.end()) + { + sLog.outError("InstanceMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode()); + assert(false); + return false; + } + + // cannot enter if the instance is full (player cap), GMs don't count + InstanceTemplate const* iTemplate = objmgr.GetInstanceTemplate(GetId()); + if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= iTemplate->maxPlayers) + { + sLog.outDetail("MAP: Instance '%u' of map '%s' cannot have more than '%u' players. Player '%s' rejected", GetInstanceId(), GetMapName(), iTemplate->maxPlayers, player->GetName()); + player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS); + return false; + } + + // cannot enter while players in the instance are in combat + Group *pGroup = player->GetGroup(); + if(pGroup && pGroup->InCombatToInstance(GetInstanceId()) && player->isAlive() && player->GetMapId() != GetId()) + { + player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT); + return false; + } + + return Map::CanEnter(player); +} + +/* + Do map specific checks and add the player to the map if successful. +*/ +bool InstanceMap::Add(Player *player) +{ + // TODO: Not sure about checking player level: already done in HandleAreaTriggerOpcode + // GMs still can teleport player in instance. + // Is it needed? + + { + Guard guard(*this); + if(!CanEnter(player)) + return false; + + // get or create an instance save for the map + InstanceSave *mapSave = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); + if(!mapSave) + { + sLog.outDetail("InstanceMap::Add: creating instance save for map %d spawnmode %d with instance id %d", GetId(), GetSpawnMode(), GetInstanceId()); + mapSave = sInstanceSaveManager.AddInstanceSave(GetId(), GetInstanceId(), GetSpawnMode(), 0, true); + } + + // check for existing instance binds + InstancePlayerBind *playerBind = player->GetBoundInstance(GetId(), GetSpawnMode()); + if(playerBind && playerBind->perm) + { + // cannot enter other instances if bound permanently + if(playerBind->save != mapSave) + { + sLog.outError("InstanceMap::Add: player %s(%d) is permanently bound to instance %d,%d,%d,%d,%d,%d but he is being put in instance %d,%d,%d,%d,%d,%d", player->GetName(), player->GetGUIDLow(), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset()); + assert(false); + } + } + else + { + Group *pGroup = player->GetGroup(); + if(pGroup) + { + // solo saves should be reset when entering a group + InstanceGroupBind *groupBind = pGroup->GetBoundInstance(GetId(), GetSpawnMode()); + if(playerBind) + { + sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d,%d,%d,%d but he is in group %d and is bound to instance %d,%d,%d,%d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), GUID_LOPART(pGroup->GetLeaderGUID()), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset()); + if(groupBind) sLog.outError("InstanceMap::Add: the group is bound to instance %d,%d,%d,%d,%d,%d", groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset()); + assert(false); + } + // bind to the group or keep using the group save + if(!groupBind) + pGroup->BindToInstance(mapSave, false); + else + { + // cannot jump to a different instance without resetting it + if(groupBind->save != mapSave) + { + sLog.outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d but he is in group %d which is bound to instance %d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), GUID_LOPART(pGroup->GetLeaderGUID()), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty()); + if(mapSave) + sLog.outError("MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount()); + else + sLog.outError("MapSave NULL"); + if(groupBind->save) + sLog.outError("GroupBind save players: %d, group count: %d", groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount()); + else + sLog.outError("GroupBind save NULL"); + assert(false); + } + // if the group/leader is permanently bound to the instance + // players also become permanently bound when they enter + if(groupBind->perm) + { + WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); + data << uint32(0); + player->GetSession()->SendPacket(&data); + player->BindToInstance(mapSave, true); + } + } + } + else + { + // set up a solo bind or continue using it + if(!playerBind) + player->BindToInstance(mapSave, false); + else + // cannot jump to a different instance without resetting it + assert(playerBind->save == mapSave); + } + } + + if(i_data) i_data->OnPlayerEnter(player); + SetResetSchedule(false); + + i_Players.push_back(player); + player->SendInitWorldStates(); + sLog.outDetail("MAP: Player '%s' entered the instance '%u' of map '%s'", player->GetName(), GetInstanceId(), GetMapName()); + // initialize unload state + m_unloadTimer = 0; + m_resetAfterUnload = false; + m_unloadWhenEmpty = false; + } + + // this will acquire the same mutex so it cannot be in the previous block + Map::Add(player); + return true; +} + +void InstanceMap::Remove(Player *player, bool remove) +{ + sLog.outDetail("MAP: Removing player '%s' from instance '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName()); + i_Players.remove(player); + SetResetSchedule(true); + if(!m_unloadTimer && i_Players.empty()) + m_unloadTimer = m_unloadWhenEmpty ? MIN_UNLOAD_DELAY : std::max(sWorld.getConfig(CONFIG_INSTANCE_UNLOAD_DELAY), (uint32)MIN_UNLOAD_DELAY); + Map::Remove(player, remove); +} + +void InstanceMap::CreateInstanceData(bool load) +{ + if(i_data != NULL) + return; + + InstanceTemplate const* mInstance = objmgr.GetInstanceTemplate(GetId()); + if (mInstance) + { + i_script = mInstance->script; + i_data = Script->CreateInstanceData(this); + } + + if(!i_data) + return; + + if(load) + { + // TODO: make a global storage for this + QueryResult* result = CharacterDatabase.PQuery("SELECT data FROM instance WHERE map = '%u' AND id = '%u'", GetId(), i_InstanceId); + if (result) + { + Field* fields = result->Fetch(); + const char* data = fields[0].GetString(); + if(data) + { + sLog.outDebug("Loading instance data for `%s` with id %u", i_script.c_str(), i_InstanceId); + i_data->Load(data); + } + delete result; + } + } + else + { + sLog.outDebug("New instance data, \"%s\" ,initialized!",i_script.c_str()); + i_data->Initialize(); + } +} + +/* + Returns true if there are no players in the instance +*/ +bool InstanceMap::Reset(uint8 method) +{ + // note: since the map may not be loaded when the instance needs to be reset + // the instance must be deleted from the DB by InstanceSaveManager + + if(!i_Players.empty()) + { + if(method == INSTANCE_RESET_ALL) + { + // notify the players to leave the instance so it can be reset + for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr) + (*itr)->SendResetFailedNotify(GetId()); + } + else + { + if(method == INSTANCE_RESET_GLOBAL) + { + // set the homebind timer for players inside (1 minute) + for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr) + (*itr)->m_InstanceValid = false; + } + + // the unload timer is not started + // instead the map will unload immediately after the players have left + m_unloadWhenEmpty = true; + m_resetAfterUnload = true; + } + } + else + { + // unloaded at next update + m_unloadTimer = MIN_UNLOAD_DELAY; + m_resetAfterUnload = true; + } + + return i_Players.empty(); +} + +uint32 InstanceMap::GetPlayersCountExceptGMs() const +{ + uint32 count = 0; + for(PlayerList::const_iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr) + if(!(*itr)->isGameMaster()) + ++count; + return count; +} + +void InstanceMap::PermBindAllPlayers(Player *player) +{ + InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); + if(!save) + { + sLog.outError("Cannot bind players, no instance save available for map!\n"); + return; + } + + Group *group = player->GetGroup(); + // group members outside the instance group don't get bound + for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr) + { + if(*itr) + { + // players inside an instance cannot be bound to other instances + // some players may already be permanently bound, in this case nothing happens + InstancePlayerBind *bind = (*itr)->GetBoundInstance(save->GetMapId(), save->GetDifficulty()); + if(!bind || !bind->perm) + { + (*itr)->BindToInstance(save, true); + WorldPacket data(SMSG_INSTANCE_SAVE_CREATED, 4); + data << uint32(0); + (*itr)->GetSession()->SendPacket(&data); + } + + // if the leader is not in the instance the group will not get a perm bind + if(group && group->GetLeaderGUID() == (*itr)->GetGUID()) + group->BindToInstance(save, true); + } + } +} + +time_t InstanceMap::GetResetTime() +{ + InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); + return save ? save->GetDifficulty() : DIFFICULTY_NORMAL; +} + +void InstanceMap::UnloadAll(bool pForce) +{ + if(!i_Players.empty()) + { + sLog.outError("InstanceMap::UnloadAll: there are still players in the instance at unload, should not happen!"); + for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr) + if(*itr) (*itr)->TeleportTo((*itr)->m_homebindMapId, (*itr)->m_homebindX, (*itr)->m_homebindY, (*itr)->m_homebindZ, (*itr)->GetOrientation()); + } + + if(m_resetAfterUnload == true) + objmgr.DeleteRespawnTimeForInstance(GetInstanceId()); + + Map::UnloadAll(pForce); +} + +void InstanceMap::SendResetWarnings(uint32 timeLeft) +{ + for(PlayerList::iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr) + (*itr)->SendInstanceResetWarning(GetId(), timeLeft); +} + +void InstanceMap::SetResetSchedule(bool on) +{ + // only for normal instances + // the reset time is only scheduled when there are no payers inside + // it is assumed that the reset time will rarely (if ever) change while the reset is scheduled + if(i_Players.empty() && !IsRaid() && !IsHeroic()) + { + InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId()); + if(!save) sLog.outError("InstanceMap::SetResetSchedule: cannot turn schedule %s, no save available for instance %d of %d", on ? "on" : "off", GetInstanceId(), GetId()); + else sInstanceSaveManager.ScheduleReset(on, save->GetResetTime(), InstanceSaveManager::InstResetEvent(0, GetId(), GetInstanceId())); + } +} + +void InstanceMap::SendToPlayers(WorldPacket const* data) const +{ + for(PlayerList::const_iterator itr = i_Players.begin(); itr != i_Players.end(); ++itr) + (*itr)->GetSession()->SendPacket(data); +} + +/* ******* Battleground Instance Maps ******* */ + +BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId) + : Map(id, expiry, InstanceId, DIFFICULTY_NORMAL) +{ +} + +BattleGroundMap::~BattleGroundMap() +{ +} + +bool BattleGroundMap::CanEnter(Player * player) +{ + if(std::find(i_Players.begin(),i_Players.end(),player)!=i_Players.end()) + { + sLog.outError("BGMap::CanEnter - player %u already in map!", player->GetGUIDLow()); + assert(false); + return false; + } + + if(player->GetBattleGroundId() != GetInstanceId()) + return false; + + // player number limit is checked in bgmgr, no need to do it here + + return Map::CanEnter(player); +} + +bool BattleGroundMap::Add(Player * player) +{ + { + Guard guard(*this); + if(!CanEnter(player)) + return false; + i_Players.push_back(player); + // reset instance validity, battleground maps do not homebind + player->m_InstanceValid = true; + } + return Map::Add(player); +} + +void BattleGroundMap::Remove(Player *player, bool remove) +{ + sLog.outDetail("MAP: Removing player '%s' from bg '%u' of map '%s' before relocating to other map", player->GetName(), GetInstanceId(), GetMapName()); + i_Players.remove(player); + Map::Remove(player, remove); +} + +void BattleGroundMap::SetUnload() +{ + m_unloadTimer = MIN_UNLOAD_DELAY; +} + +void BattleGroundMap::UnloadAll(bool pForce) +{ + while(!i_Players.empty()) + { + PlayerList::iterator itr = i_Players.begin(); + Player * plr = *itr; + if(plr) (plr)->TeleportTo((*itr)->m_homebindMapId, (*itr)->m_homebindX, (*itr)->m_homebindY, (*itr)->m_homebindZ, (*itr)->GetOrientation()); + // TeleportTo removes the player from this map (if the map exists) -> calls BattleGroundMap::Remove -> invalidates the iterator. + // just in case, remove the player from the list explicitly here as well to prevent a possible infinite loop + // note that this remove is not needed if the code works well in other places + i_Players.remove(plr); + } + + Map::UnloadAll(pForce); +} diff --git a/src/game/Map.h b/src/game/Map.h new file mode 100644 index 00000000000..bc723e4bca5 --- /dev/null +++ b/src/game/Map.h @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_MAP_H +#define MANGOS_MAP_H + +#include "Platform/Define.h" +#include "Policies/ThreadingModel.h" +#include "zthread/Lockable.h" +#include "zthread/Mutex.h" +#include "zthread/FairReadWriteLock.h" +#include "Database/DBCStructure.h" +#include "GridDefines.h" +#include "Cell.h" +#include "Object.h" +#include "Timer.h" +#include "SharedDefines.h" +#include "GameSystem/GridRefManager.h" + +#include +#include + +class Unit; +class WorldPacket; +class InstanceData; +class Group; +class InstanceSave; + +namespace ZThread +{ + class Lockable; + class ReadWriteLock; +} + +typedef ZThread::FairReadWriteLock GridRWLock; + +template +struct RGuard +{ + RGuard(MUTEX &l) : i_lock(l.getReadLock()) {} + MaNGOS::GeneralLock i_lock; +}; + +template +struct WGuard +{ + WGuard(MUTEX &l) : i_lock(l.getWriteLock()) {} + MaNGOS::GeneralLock i_lock; +}; + +typedef RGuard GridReadGuard; +typedef WGuard GridWriteGuard; +typedef MaNGOS::SingleThreaded::Lock NullGuard; + +typedef struct +{ + uint16 area_flag[16][16]; + uint8 terrain_type[16][16]; + float liquid_level[128][128]; + float Z[MAP_RESOLUTION][MAP_RESOLUTION]; +}GridMap; + +struct CreatureMover +{ + CreatureMover() : x(0), y(0), z(0), ang(0) {} + CreatureMover(float _x, float _y, float _z, float _ang) : x(_x), y(_y), z(_z), ang(_ang) {} + + float x, y, z, ang; +}; + +// 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 + +struct InstanceTemplate +{ + uint32 map; + uint32 parent; + uint32 levelMin; + uint32 levelMax; + uint32 maxPlayers; + uint32 reset_delay; + float startLocX; + float startLocY; + float startLocZ; + float startLocO; + char const* script; +}; + +enum LevelRequirementVsMode +{ + LEVELREQUIREMENT_HEROIC = 70 +}; + +#if defined( __GNUC__ ) +#pragma pack() +#else +#pragma pack(pop) +#endif + +typedef HM_NAMESPACE::hash_map CreatureMoveList; + +#define MAX_HEIGHT 100000.0f // can be use for find ground height at surface +#define INVALID_HEIGHT -100000.0f // for check, must be equal to VMAP_INVALID_HEIGHT, real value for unknown height is VMAP_INVALID_HEIGHT_VALUE +#define MIN_UNLOAD_DELAY 1 // immediate unload + +class MANGOS_DLL_SPEC Map : public GridRefManager, public MaNGOS::ObjectLevelLockable +{ + public: + Map(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode); + virtual ~Map(); + + // currently unused for normal maps + virtual bool CanUnload(const uint32& diff); + + virtual bool Add(Player *); + virtual void Remove(Player *, bool); + template void Add(T *); + template void Remove(T *, bool); + + virtual void Update(const uint32&); + + void MessageBroadcast(Player *, WorldPacket *, bool to_self); + void MessageBroadcast(WorldObject *, WorldPacket *); + void MessageDistBroadcast(Player *, WorldPacket *, float dist, bool to_self, bool own_team_only = false); + void MessageDistBroadcast(WorldObject *, WorldPacket *, float dist); + + void PlayerRelocation(Player *, float x, float y, float z, float angl); + void CreatureRelocation(Creature *creature, float x, float y, float, float); + + template void Visit(const CellLock &cell, TypeContainerVisitor &visitor); + + inline bool IsRemovalGrid(float x, float y) const + { + GridPair p = MaNGOS::ComputeGridPair(x, y); + return( !getNGrid(p.x_coord, p.y_coord) || getNGrid(p.x_coord, p.y_coord)->GetGridState() == GRID_STATE_REMOVAL ); + } + + bool GetUnloadFlag(const GridPair &p) const { return getNGrid(p.x_coord, p.y_coord)->getUnloadFlag(); } + void SetUnloadFlag(const GridPair &p, bool unload) { getNGrid(p.x_coord, p.y_coord)->setUnloadFlag(unload); } + void LoadGrid(const Cell& cell, bool no_unload = false); + bool UnloadGrid(const uint32 &x, const uint32 &y, bool pForce); + virtual void UnloadAll(bool pForce); + + void ResetGridExpiry(NGridType &grid, float factor = 1) const + { + grid.ResetTimeTracker((time_t)((float)i_gridExpiry*factor)); + } + + time_t GetGridExpiry(void) const { return i_gridExpiry; } + uint32 GetId(void) const { return i_id; } + + static bool ExistMap(uint32 mapid, int x, int y); + static bool ExistVMap(uint32 mapid, int x, int y); + void LoadMapAndVMap(uint32 mapid, uint32 instanceid, int x, int y); + + static void InitStateMachine(); + static void DeleteStateMachine(); + + // some calls like isInWater should not use vmaps due to processor power + // can return INVALID_HEIGHT if under z+2 z coord not found height + float GetHeight(float x, float y, float z, bool pCheckVMap=true) const; + bool IsInWater(float x, float y, float z) const; // does not use z pos. This is for future use + + uint16 GetAreaFlag(float x, float y ) const; + uint8 GetTerrainType(float x, float y ) const; + float GetWaterLevel(float x, float y ) const; + bool IsUnderWater(float x, float y, float z) const; + + static uint32 GetAreaId(uint16 areaflag,uint32 map_id); + static uint32 GetZoneId(uint16 areaflag,uint32 map_id); + + uint32 GetAreaId(float x, float y) const + { + return GetAreaId(GetAreaFlag(x,y),i_id); + } + + uint32 GetZoneId(float x, float y) const + { + return GetZoneId(GetAreaFlag(x,y),i_id); + } + + virtual void MoveAllCreaturesInMoveList(); + virtual void RemoveAllObjectsInRemoveList(); + + bool CreatureRespawnRelocation(Creature *c); // used only in MoveAllCreaturesInMoveList and ObjectGridUnloader + + // assert print helper + bool CheckGridIntegrity(Creature* c, bool moved) const; + + uint32 GetInstanceId() { return i_InstanceId; } + uint8 GetSpawnMode() { return (i_spawnMode); } + virtual bool CanEnter(Player* /*player*/) { return true; } + const char* GetMapName() const; + + bool Instanceable() const { return i_mapEntry && i_mapEntry->Instanceable(); } + // NOTE: this duplicate of Instanceable(), but Instanceable() can be changed when BG also will be instanceable + bool IsDungeon() const { return i_mapEntry && i_mapEntry->IsDungeon(); } + bool IsRaid() const { return i_mapEntry && i_mapEntry->IsRaid(); } + bool IsHeroic() const { return i_spawnMode == DIFFICULTY_HEROIC; } + bool IsBattleGround() const { return i_mapEntry && i_mapEntry->IsBattleGround(); } + bool IsBattleArena() const { return i_mapEntry && i_mapEntry->IsBattleArena(); } + bool IsBattleGroundOrArena() const { return i_mapEntry && i_mapEntry->IsBattleGroundOrArena(); } + + void AddObjectToRemoveList(WorldObject *obj); + void DoDelayedMovesAndRemoves(); + + virtual bool RemoveBones(uint64 guid, float x, float y); + + void UpdateObjectVisibility(WorldObject* obj, Cell cell, CellPair cellpair); + void UpdatePlayerVisibility(Player* player, Cell cell, CellPair cellpair); + void UpdateObjectsVisibilityFor(Player* player, Cell cell, CellPair cellpair); + + void resetMarkedCells() { marked_cells.reset(); } + bool isCellMarked(uint32 pCellId) { return marked_cells.test(pCellId); } + void markCell(uint32 pCellId) { marked_cells.set(pCellId); } + private: + void LoadVMap(int pX, int pY); + void LoadMap(uint32 mapid, uint32 instanceid, int x,int y); + + void SetTimer(uint32 t) { i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t; } + //uint64 CalculateGridMask(const uint32 &y) const; + + void SendInitSelf( Player * player ); + + void SendInitTransports( Player * player ); + void SendRemoveTransports( Player * player ); + + void PlayerRelocationNotify(Player* player, Cell cell, CellPair cellpair); + void CreatureRelocationNotify(Creature *creature, Cell newcell, CellPair newval); + + bool CreatureCellRelocation(Creature *creature, Cell new_cell); + + void AddCreatureToMoveList(Creature *c, float x, float y, float z, float ang); + CreatureMoveList i_creaturesToMove; + + bool loaded(const GridPair &) const; + void EnsureGridLoadedForPlayer(const Cell&, Player*, bool add_player); + void EnsureGridCreated(const GridPair &); + + void buildNGridLinkage(NGridType* pNGridType) { pNGridType->link(this); } + + template void AddType(T *obj); + template void RemoveType(T *obj, bool); + + NGridType* getNGrid(uint32 x, uint32 y) const + { + return i_grids[x][y]; + } + + bool isGridObjectDataLoaded(uint32 x, uint32 y) const { return getNGrid(x,y)->isGridObjectDataLoaded(); } + void setGridObjectDataLoaded(bool pLoaded, uint32 x, uint32 y) { getNGrid(x,y)->setGridObjectDataLoaded(pLoaded); } + + inline void setNGrid(NGridType* grid, uint32 x, uint32 y); + + protected: + typedef MaNGOS::ObjectLevelLockable::Lock Guard; + + MapEntry const* i_mapEntry; + uint8 i_spawnMode; + uint32 i_id; + uint32 i_InstanceId; + uint32 m_unloadTimer; + + private: + typedef GridReadGuard ReadGuard; + typedef GridWriteGuard WriteGuard; + + NGridType* i_grids[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; + GridMap *GridMaps[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; + std::bitset marked_cells; + + time_t i_gridExpiry; + + std::set i_objectsToRemove; + + // Type specific code for add/remove to/from grid + template + void AddToGrid(T*, NGridType *, Cell const&); + + template + void AddNotifier(T*, Cell const&, CellPair const&); + + template + void RemoveFromGrid(T*, NGridType *, Cell const&); + + template + void DeleteFromWorld(T*); +}; + +enum InstanceResetMethod +{ + INSTANCE_RESET_ALL, + INSTANCE_RESET_CHANGE_DIFFICULTY, + INSTANCE_RESET_GLOBAL, + INSTANCE_RESET_GROUP_DISBAND, + INSTANCE_RESET_GROUP_JOIN, + INSTANCE_RESET_RESPAWN_DELAY +}; + +class MANGOS_DLL_SPEC InstanceMap : public Map +{ + public: + typedef std::list PlayerList; // online players only + + InstanceMap(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode); + ~InstanceMap(); + bool Add(Player *); + void Remove(Player *, bool); + void Update(const uint32&); + void CreateInstanceData(bool load); + bool Reset(uint8 method); + std::string GetScript() { return i_script; } + InstanceData* GetInstanceData() { return i_data; } + void PermBindAllPlayers(Player *player); + PlayerList const& GetPlayers() const { return i_Players;} + void SendToPlayers(WorldPacket const* data) const; + time_t GetResetTime(); + void UnloadAll(bool pForce); + bool CanEnter(Player* player); + uint32 GetPlayersCountExceptGMs() const; + uint32 HavePlayers() const { return !i_Players.empty(); } + void SendResetWarnings(uint32 timeLeft); + void SetResetSchedule(bool on); + private: + bool m_resetAfterUnload; + bool m_unloadWhenEmpty; + InstanceData* i_data; + std::string i_script; + // only online players that are inside the instance currently + // TODO ? - use the grid instead to access the players + PlayerList i_Players; +}; + +class MANGOS_DLL_SPEC BattleGroundMap : public Map +{ + public: + typedef std::list PlayerList; // online players only + + BattleGroundMap(uint32 id, time_t, uint32 InstanceId); + ~BattleGroundMap(); + + bool Add(Player *); + void Remove(Player *, bool); + bool CanEnter(Player* player); + void SetUnload(); + void UnloadAll(bool pForce); + private: + PlayerList i_Players; +}; + +/*inline +uint64 +Map::CalculateGridMask(const uint32 &y) const +{ + uint64 mask = 1; + mask <<= y; + return mask; +} +*/ + +template +inline void +Map::Visit(const CellLock &cell, TypeContainerVisitor &visitor) +{ + const uint32 x = cell->GridX(); + const uint32 y = cell->GridY(); + const uint32 cell_x = cell->CellX(); + const uint32 cell_y = cell->CellY(); + + if( !cell->NoCreate() || loaded(GridPair(x,y)) ) + { + EnsureGridLoadedForPlayer(cell, NULL, false); + //LOCK_TYPE guard(i_info[x][y]->i_lock); + getNGrid(x, y)->Visit(cell_x, cell_y, visitor); + } +} +#endif diff --git a/src/game/MapInstanced.cpp b/src/game/MapInstanced.cpp new file mode 100644 index 00000000000..2ff105c2f9d --- /dev/null +++ b/src/game/MapInstanced.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "MapInstanced.h" +#include "ObjectMgr.h" +#include "MapManager.h" +#include "BattleGround.h" +#include "VMapFactory.h" +#include "InstanceSaveMgr.h" +#include "World.h" + +MapInstanced::MapInstanced(uint32 id, time_t expiry, uint32 aInstanceId) : Map(id, expiry, 0, 0) +{ + // initialize instanced maps list + m_InstancedMaps.clear(); + // fill with zero + memset(&GridMapReference, 0, MAX_NUMBER_OF_GRIDS*MAX_NUMBER_OF_GRIDS*sizeof(uint16)); +} + +void MapInstanced::Update(const uint32& t) +{ + // take care of loaded GridMaps (when unused, unload it!) + Map::Update(t); + + // update the instanced maps + InstancedMaps::iterator i = m_InstancedMaps.begin(); + + while (i != m_InstancedMaps.end()) + { + if(i->second->CanUnload(t)) + { + DestroyInstance(i); // iterator incremented + } + else + { + // update only here, because it may schedule some bad things before delete + i->second->Update(t); + ++i; + } + } +} + +void MapInstanced::MoveAllCreaturesInMoveList() +{ + for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++) + { + i->second->MoveAllCreaturesInMoveList(); + } + + Map::MoveAllCreaturesInMoveList(); +} + +void MapInstanced::RemoveAllObjectsInRemoveList() +{ + for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++) + { + i->second->RemoveAllObjectsInRemoveList(); + } + + Map::RemoveAllObjectsInRemoveList(); +} + +bool MapInstanced::RemoveBones(uint64 guid, float x, float y) +{ + bool remove_result = false; + + for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++) + { + remove_result = remove_result || i->second->RemoveBones(guid, x, y); + } + + return remove_result || Map::RemoveBones(guid,x,y); +} + +void MapInstanced::UnloadAll(bool pForce) +{ + // Unload instanced maps + for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++) + i->second->UnloadAll(pForce); + + // Delete the maps only after everything is unloaded to prevent crashes + for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); i++) + delete i->second; + + m_InstancedMaps.clear(); + + // Unload own grids (just dummy(placeholder) grids, neccesary to unload GridMaps!) + Map::UnloadAll(pForce); +} + +/* +- return the right instance for the object, based on its InstanceId +- create the instance if it's not created already +- the player is not actually added to the instance (only in InstanceMap::Add) +*/ +Map* MapInstanced::GetInstance(const WorldObject* obj) +{ + uint32 CurInstanceId = obj->GetInstanceId(); + Map* map = NULL; + + if (obj->GetMapId() == GetId() && CurInstanceId != 0) + { + // the object wants to be put in a certain instance of this map + map = _FindMap(CurInstanceId); + if(!map) + { + // For players if the instanceId is set, it's assumed they are already in a map, + // hence the map must be loaded. For Creatures, GameObjects etc the map must exist + // prior to calling GetMap, they are not allowed to create maps for themselves. + sLog.outError("GetInstance: object %s(%d), typeId %d, in world %d, should be in map %d,%d but that's not loaded yet.", obj->GetName(), obj->GetGUIDLow(), obj->GetTypeId(), obj->IsInWorld(), obj->GetMapId(), obj->GetInstanceId()); + assert(false); + } + return(map); + } + else + { + // instance not specified, find an existing or create a new one + if(obj->GetTypeId() != TYPEID_PLAYER) + { + sLog.outError("MAPINSTANCED: WorldObject '%u' (Entry: %u TypeID: %u) is in map %d,%d and requested base map instance of map %d, this must not happen", obj->GetGUIDLow(), obj->GetEntry(), obj->GetTypeId(), obj->GetMapId(), obj->GetInstanceId(), GetId()); + assert(false); + return NULL; + } + else + { + uint32 NewInstanceId = 0; // instanceId of the resulting map + Player* player = (Player*)obj; + + // TODO: battlegrounds and arenas + + InstancePlayerBind *pBind = player->GetBoundInstance(GetId(), player->GetDifficulty()); + InstanceSave *pSave = pBind ? pBind->save : NULL; + + // the player's permanet player bind is taken into consideration first + // then the player's group bind and finally the solo bind. + if(!pBind || !pBind->perm) + { + InstanceGroupBind *groupBind = NULL; + Group *group = player->GetGroup(); + // use the player's difficulty setting (it may not be the same as the group's) + if(group && (groupBind = group->GetBoundInstance(GetId(), player->GetDifficulty()))) + pSave = groupBind->save; + } + + if(pSave) + { + // solo/perm/group + NewInstanceId = pSave->GetInstanceId(); + map = _FindMap(NewInstanceId); + // it is possible that the save exists but the map doesn't + if(!map) + map = CreateInstance(NewInstanceId, pSave, pSave->GetDifficulty()); + return map; + } + else + { + // if no instanceId via group members or instance saves is found + // the instance will be created for the first time + NewInstanceId = MapManager::Instance().GenerateInstanceId(); + return CreateInstance(NewInstanceId, NULL, player->GetDifficulty()); + } + } + } +} + +InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave *save, uint8 difficulty) +{ + // load/create a map + Guard guard(*this); + + // make sure we have a valid map id + const MapEntry* entry = sMapStore.LookupEntry(GetId()); + if(!entry) + { + sLog.outError("CreateInstance: no entry for map %d", GetId()); + assert(false); + } + const InstanceTemplate * iTemplate = objmgr.GetInstanceTemplate(GetId()); + if(!iTemplate) + { + sLog.outError("CreateInstance: no instance template for map %d", GetId()); + assert(false); + } + + // some instances only have one difficulty + if(!entry->SupportsHeroicMode()) difficulty = DIFFICULTY_NORMAL; + + sLog.outDebug("MapInstanced::CreateInstance: %smap instance %d for %d created with difficulty %s", save?"":"new ", InstanceId, GetId(), difficulty?"heroic":"normal"); + + InstanceMap *map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty); + assert(map->IsDungeon()); + + bool load_data = save != NULL; + map->CreateInstanceData(load_data); + + m_InstancedMaps[InstanceId] = map; + return map; +} + +BattleGroundMap* MapInstanced::CreateBattleGround(uint32 InstanceId) +{ + // load/create a map + Guard guard(*this); + + sLog.outDebug("MapInstanced::CreateBattleGround: map bg %d for %d created.", InstanceId, GetId()); + + BattleGroundMap *map = new BattleGroundMap(GetId(), GetGridExpiry(), InstanceId); + assert(map->IsBattleGroundOrArena()); + + m_InstancedMaps[InstanceId] = map; + return map; +} + +void MapInstanced::DestroyInstance(uint32 InstanceId) +{ + InstancedMaps::iterator itr = m_InstancedMaps.find(InstanceId); + if(itr != m_InstancedMaps.end()) + DestroyInstance(itr); +} + +// increments the iterator after erase +void MapInstanced::DestroyInstance(InstancedMaps::iterator &itr) +{ + itr->second->UnloadAll(true); + // should only unload VMaps if this is the last instance and grid unloading is enabled + if(m_InstancedMaps.size() <= 1 && sWorld.getConfig(CONFIG_GRID_UNLOAD)) + { + VMAP::VMapFactory::createOrGetVMapManager()->unloadMap(itr->second->GetId()); + // in that case, unload grids of the base map, too + // so in the next map creation, (EnsureGridCreated actually) VMaps will be reloaded + Map::UnloadAll(true); + } + // erase map + delete itr->second; + m_InstancedMaps.erase(itr++); +} + diff --git a/src/game/MapInstanced.h b/src/game/MapInstanced.h new file mode 100644 index 00000000000..58ee6b7a9bc --- /dev/null +++ b/src/game/MapInstanced.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_MAP_INSTANCED_H +#define MANGOS_MAP_INSTANCED_H + +#include "Map.h" +#include "InstanceSaveMgr.h" + +class MANGOS_DLL_DECL MapInstanced : public Map +{ + friend class MapManager; + public: + typedef HM_NAMESPACE::hash_map< uint32, Map* > InstancedMaps; + + MapInstanced(uint32 id, time_t, uint32 aInstanceId); + ~MapInstanced() {} + + // functions overwrite Map versions + void Update(const uint32&); + void MoveAllCreaturesInMoveList(); + void RemoveAllObjectsInRemoveList(); + bool RemoveBones(uint64 guid, float x, float y); + void UnloadAll(bool pForce); + + Map* GetInstance(const WorldObject* obj); + Map* FindMap(uint32 InstanceId) { return _FindMap(InstanceId); } + void DestroyInstance(uint32 InstanceId); + void DestroyInstance(InstancedMaps::iterator &itr); + void AddGridMapReference(const GridPair &p) { ++GridMapReference[p.x_coord][p.y_coord]; } + void RemoveGridMapReference(const GridPair &p) + { + --GridMapReference[p.x_coord][p.y_coord]; + if (!GridMapReference[p.x_coord][p.y_coord]) { SetUnloadFlag(GridPair(63-p.x_coord,63-p.y_coord), true); } + } + + InstancedMaps &GetInstancedMaps() { return m_InstancedMaps; } + + private: + + InstanceMap* CreateInstance(uint32 InstanceId, InstanceSave *save, uint8 difficulty); + BattleGroundMap* CreateBattleGround(uint32 InstanceId); + + InstancedMaps m_InstancedMaps; + + Map* _FindMap(uint32 InstanceId) + { + InstancedMaps::iterator i = m_InstancedMaps.find(InstanceId); + + return(i == m_InstancedMaps.end() ? NULL : i->second); + } + + uint16 GridMapReference[MAX_NUMBER_OF_GRIDS][MAX_NUMBER_OF_GRIDS]; +}; +#endif diff --git a/src/game/MapManager.cpp b/src/game/MapManager.cpp new file mode 100644 index 00000000000..84c13333ddf --- /dev/null +++ b/src/game/MapManager.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "MapManager.h" +#include "InstanceSaveMgr.h" +#include "Policies/SingletonImp.h" +#include "Database/DatabaseEnv.h" +#include "Log.h" +#include "ObjectAccessor.h" +#include "Transports.h" +#include "GridDefines.h" +#include "MapInstanced.h" +#include "DestinationHolderImp.h" +#include "World.h" +#include "CellImpl.h" +#include "Corpse.h" +#include "ObjectMgr.h" + +#define CLASS_LOCK MaNGOS::ClassLevelLockable +INSTANTIATE_SINGLETON_2(MapManager, CLASS_LOCK); +INSTANTIATE_CLASS_MUTEX(MapManager, ZThread::Mutex); + +extern GridState* si_GridStates[]; // debugging code, should be deleted some day + +MapManager::MapManager() : i_gridCleanUpDelay(sWorld.getConfig(CONFIG_INTERVAL_GRIDCLEAN)) +{ + i_timer.SetInterval(sWorld.getConfig(CONFIG_INTERVAL_MAPUPDATE)); +} + +MapManager::~MapManager() +{ + for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter) + delete iter->second; + + for(TransportSet::iterator i = m_Transports.begin(); i != m_Transports.end(); ++i) + delete *i; + + Map::DeleteStateMachine(); +} + +void +MapManager::Initialize() +{ + Map::InitStateMachine(); + + // debugging code, should be deleted some day + { + for(int i=0;icheckMagic()) + { + ok = false; + si_GridStates[i]->setMagic(); + } + #endif + } + if(!ok) + ++i_GridStateErrorCount; + if(i_GridStateErrorCount > 2) + assert(false); // force a crash. Too many errors +} + +Map* +MapManager::_GetBaseMap(uint32 id) +{ + Map *m = _findMap(id); + + if( m == NULL ) + { + Guard guard(*this); + + const MapEntry* entry = sMapStore.LookupEntry(id); + if (entry && entry->IsDungeon()) + { + m = new MapInstanced(id, i_gridCleanUpDelay, 0); + } + else + { + m = new Map(id, i_gridCleanUpDelay, 0, 0); + } + i_maps[id] = m; + } + + assert(m != NULL); + return m; +} + +Map* MapManager::GetMap(uint32 id, const WorldObject* obj) +{ + //if(!obj->IsInWorld()) sLog.outError("GetMap: called for map %d with object (typeid %d, guid %d, mapid %d, instanceid %d) who is not in world!", id, obj->GetTypeId(), obj->GetGUIDLow(), obj->GetMapId(), obj->GetInstanceId()); + Map *m = _GetBaseMap(id); + + if (m && obj && m->Instanceable()) m = ((MapInstanced*)m)->GetInstance(obj); + + return m; +} + +Map* MapManager::FindMap(uint32 mapid, uint32 instanceId) +{ + Map *map = FindMap(mapid); + if(!map) return NULL; + if(!map->Instanceable()) return instanceId == 0 ? map : NULL; + return ((MapInstanced*)map)->FindMap(instanceId); +} + +/* + checks that do not require a map to be created + will send transfer error messages on fail +*/ +bool MapManager::CanPlayerEnter(uint32 mapid, Player* player) +{ + const MapEntry *entry = sMapStore.LookupEntry(mapid); + if(!entry) return false; + const char *mapName = entry->name[player->GetSession()->GetSessionDbcLocale()]; + + if(entry->map_type == MAP_INSTANCE || entry->map_type == MAP_RAID) + { + if (entry->map_type == MAP_RAID) + { + // GMs can avoid raid limitations + if(!player->isGameMaster() && !sWorld.getConfig(CONFIG_INSTANCE_IGNORE_RAID)) + { + // can only enter in a raid group + Group* group = player->GetGroup(); + if (!group || !group->isRaidGroup()) + { + // probably there must be special opcode, because client has this string constant in GlobalStrings.lua + // TODO: this is not a good place to send the message + player->GetSession()->SendAreaTriggerMessage("You must be in a raid group to enter %s instance", mapName); + sLog.outDebug("MAP: Player '%s' must be in a raid group to enter instance of '%s'", player->GetName(), mapName); + return false; + } + } + } + + //The player has a heroic mode and tries to enter into instance which has no a heroic mode + if (!entry->SupportsHeroicMode() && player->GetDifficulty() == DIFFICULTY_HEROIC) + { + player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY2); //Send aborted message + return false; + } + + if (!player->isAlive()) + { + if(Corpse *corpse = player->GetCorpse()) + { + // let enter in ghost mode in instance that connected to inner instance with corpse + uint32 instance_map = corpse->GetMapId(); + do + { + if(instance_map==mapid) + break; + + InstanceTemplate const* instance = objmgr.GetInstanceTemplate(instance_map); + instance_map = instance ? instance->parent : 0; + } + while (instance_map); + + if (!instance_map) + { + player->GetSession()->SendAreaTriggerMessage("You cannot enter %s while in a ghost mode", mapName); + sLog.outDebug("MAP: Player '%s' doesn't has a corpse in instance '%s' and can't enter", player->GetName(), mapName); + return false; + } + sLog.outDebug("MAP: Player '%s' has corpse in instance '%s' and can enter", player->GetName(), mapName); + } + else + { + sLog.outDebug("Map::CanEnter - player '%s' is dead but doesn't have a corpse!", player->GetName()); + } + } + + // TODO: move this to a map dependent location + /*if(i_data && i_data->IsEncounterInProgress()) + { + sLog.outDebug("MAP: Player '%s' can't enter instance '%s' while an encounter is in progress.", player->GetName(), GetMapName()); + player->SendTransferAborted(GetId(), TRANSFER_ABORT_ZONE_IN_COMBAT); + return(false); + }*/ + return true; + } + else + return true; +} + +void MapManager::DeleteInstance(uint32 mapid, uint32 instanceId, uint8 mode) +{ + Map *m = _GetBaseMap(mapid); + if (m && m->Instanceable()) + ((MapInstanced*)m)->DestroyInstance(instanceId); +} + +void MapManager::RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y) +{ + bool remove_result = _GetBaseMap(mapid)->RemoveBones(guid, x, y); + + if (!remove_result) + { + sLog.outDebug("Bones %u not found in world. Delete from DB also.", GUID_LOPART(guid)); + } +} + +void +MapManager::Update(time_t diff) +{ + i_timer.Update(diff); + if( !i_timer.Passed() ) + return; + + for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter) + { + checkAndCorrectGridStatesArray(); // debugging code, should be deleted some day + iter->second->Update(i_timer.GetCurrent()); + } + + ObjectAccessor::Instance().Update(i_timer.GetCurrent()); + for (TransportSet::iterator iter = m_Transports.begin(); iter != m_Transports.end(); ++iter) + (*iter)->Update(i_timer.GetCurrent()); + + i_timer.SetCurrent(0); +} + +void MapManager::DoDelayedMovesAndRemoves() +{ + for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter) + iter->second->DoDelayedMovesAndRemoves(); +} + +bool MapManager::ExistMapAndVMap(uint32 mapid, float x,float y) +{ + GridPair p = MaNGOS::ComputeGridPair(x,y); + + int gx=63-p.x_coord; + int gy=63-p.y_coord; + + return Map::ExistMap(mapid,gx,gy) && Map::ExistVMap(mapid,gx,gy); +} + +bool MapManager::IsValidMAP(uint32 mapid) +{ + MapEntry const* mEntry = sMapStore.LookupEntry(mapid); + return mEntry && (!mEntry->Instanceable() || objmgr.GetInstanceTemplate(mapid)); +} + +void MapManager::LoadGrid(int mapid, float x, float y, const WorldObject* obj, bool no_unload) +{ + CellPair p = MaNGOS::ComputeCellPair(x,y); + Cell cell(p); + GetMap(mapid, obj)->LoadGrid(cell,no_unload); +} + +void MapManager::UnloadAll() +{ + for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter) + iter->second->UnloadAll(true); + + while(!i_maps.empty()) + { + delete i_maps.begin()->second; + i_maps.erase(i_maps.begin()); + } +} + +void MapManager::InitMaxInstanceId() +{ + i_MaxInstanceId = 0; + + QueryResult *result = CharacterDatabase.Query( "SELECT MAX(id) FROM instance" ); + if( result ) + { + i_MaxInstanceId = result->Fetch()[0].GetUInt32(); + delete result; + } +} + +uint32 MapManager::GetNumInstances() +{ + uint32 ret = 0; + for(MapMapType::iterator itr = i_maps.begin(); itr != i_maps.end(); ++itr) + { + Map *map = itr->second; + if(!map->Instanceable()) continue; + MapInstanced::InstancedMaps &maps = ((MapInstanced *)map)->GetInstancedMaps(); + for(MapInstanced::InstancedMaps::iterator mitr = maps.begin(); mitr != maps.end(); ++mitr) + if(mitr->second->IsDungeon()) ret++; + } + return ret; +} + +uint32 MapManager::GetNumPlayersInInstances() +{ + uint32 ret = 0; + for(MapMapType::iterator itr = i_maps.begin(); itr != i_maps.end(); ++itr) + { + Map *map = itr->second; + if(!map->Instanceable()) continue; + MapInstanced::InstancedMaps &maps = ((MapInstanced *)map)->GetInstancedMaps(); + for(MapInstanced::InstancedMaps::iterator mitr = maps.begin(); mitr != maps.end(); ++mitr) + if(mitr->second->IsDungeon()) + ret += ((InstanceMap*)mitr->second)->GetPlayers().size(); + } + return ret; +} diff --git a/src/game/MapManager.h b/src/game/MapManager.h new file mode 100644 index 00000000000..833f14dfc62 --- /dev/null +++ b/src/game/MapManager.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_MAPMANAGER_H +#define MANGOS_MAPMANAGER_H + +#include "Platform/Define.h" +#include "Policies/Singleton.h" +#include "zthread/Mutex.h" +#include "Common.h" +#include "Map.h" +#include "GridStates.h" +class Transport; + +class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton > +{ + + friend class MaNGOS::OperatorNew; + typedef HM_NAMESPACE::hash_map MapMapType; + typedef std::pair::iterator, bool> MapMapPair; + + public: + + Map* GetMap(uint32, const WorldObject* obj); + Map* FindMap(uint32 mapid) { return _findMap(mapid); } + Map* FindMap(uint32 mapid, uint32 instanceId); + + // only const version for outer users + Map const* GetBaseMap(uint32 id) const { return const_cast(this)->_GetBaseMap(id); } + void DeleteInstance(uint32 mapid, uint32 instanceId, uint8 mode); + + inline uint16 GetAreaFlag(uint32 mapid, float x, float y) const + { + Map const* m = GetBaseMap(mapid); + return m->GetAreaFlag(x, y); + } + inline uint32 GetAreaId(uint32 mapid, float x, float y) { return Map::GetAreaId(GetAreaFlag(mapid, x, y),mapid); } + inline uint32 GetZoneId(uint32 mapid, float x, float y) { return Map::GetZoneId(GetAreaFlag(mapid, x, y),mapid); } + + void Initialize(void); + void Update(time_t); + + inline void SetGridCleanUpDelay(uint32 t) + { + if( t < MIN_GRID_DELAY ) + i_gridCleanUpDelay = MIN_GRID_DELAY; + else + i_gridCleanUpDelay = t; + } + + inline void SetMapUpdateInterval(uint32 t) + { + if( t > MIN_MAP_UPDATE_DELAY ) + t = MIN_MAP_UPDATE_DELAY; + + i_timer.SetInterval(t); + i_timer.Reset(); + } + + void LoadGrid(int mapid, float x, float y, const WorldObject* obj, bool no_unload = false); + void UnloadAll(); + + static bool ExistMapAndVMap(uint32 mapid, float x, float y); + static bool IsValidMAP(uint32 mapid); + + static bool IsValidMapCoord(uint32 mapid, float x,float y) + { + return IsValidMAP(mapid) && MaNGOS::IsValidMapCoord(x,y); + } + + static bool IsValidMapCoord(uint32 mapid, float x,float y,float z) + { + return IsValidMAP(mapid) && MaNGOS::IsValidMapCoord(x,y,z); + } + + static bool IsValidMapCoord(uint32 mapid, float x,float y,float z,float o) + { + return IsValidMAP(mapid) && MaNGOS::IsValidMapCoord(x,y,z,o); + } + + void DoDelayedMovesAndRemoves(); + + void LoadTransports(); + + typedef std::set TransportSet; + TransportSet m_Transports; + + typedef std::map TransportMap; + TransportMap m_TransportsByMap; + + bool CanPlayerEnter(uint32 mapid, Player* player); + void RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y); + inline uint32 GenerateInstanceId() { return ++i_MaxInstanceId; } + void InitMaxInstanceId(); + + /* statistics */ + uint32 GetNumInstances(); + uint32 GetNumPlayersInInstances(); + + private: + // debugging code, should be deleted some day + void checkAndCorrectGridStatesArray(); // just for debugging to find some memory overwrites + GridState* i_GridStates[MAX_GRID_STATE]; // shadow entries to the global array in Map.cpp + int i_GridStateErrorCount; + private: + MapManager(); + ~MapManager(); + + MapManager(const MapManager &); + MapManager& operator=(const MapManager &); + + Map* _GetBaseMap(uint32 id); + Map* _findMap(uint32 id) const + { + MapMapType::const_iterator iter = i_maps.find(id); + return (iter == i_maps.end() ? NULL : iter->second); + } + + typedef MaNGOS::ClassLevelLockable::Lock Guard; + uint32 i_gridCleanUpDelay; + MapMapType i_maps; + IntervalTimer i_timer; + + uint32 i_MaxInstanceId; +}; +#endif diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp new file mode 100644 index 00000000000..092795c6fbe --- /dev/null +++ b/src/game/MiscHandler.cpp @@ -0,0 +1,1761 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 +#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::MapType& m = ObjectAccessor::Instance().GetPlayers(); + for(HashMapHolder::MapType::iterator itr = m.begin(); itr != m.end(); ++itr) + { + if (security == SEC_PLAYER) + { + // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST + if (itr->second->GetTeam() != team && !allowTwoSideWhoList ) + continue; + + // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST + if ((itr->second->GetSession()->GetSecurity() > SEC_PLAYER && !gmInWhoList)) + continue; + } + + // check if target is globally visible for player + if (!(itr->second->IsVisibleGloballyFor(_player))) + continue; + + // check if target's level is in level range + uint32 lvl = itr->second->getLevel(); + if (lvl < level_min || lvl > level_max) + continue; + + // check if class matches classmask + uint32 class_ = itr->second->getClass(); + if (!(classmask & (1 << class_))) + continue; + + // check if race matches racemask + uint32 race = itr->second->getRace(); + if (!(racemask & (1 << race))) + continue; + + uint32 pzoneid = itr->second->GetZoneId(); + + bool z_show = true; + for(uint32 i = 0; i < zones_count; i++) + { + if(zoneids[i] == pzoneid) + { + z_show = true; + break; + } + + z_show = false; + } + if (!z_show) + continue; + + std::string pname = itr->second->GetName(); + std::wstring wpname; + if(!Utf8toWStr(pname,wpname)) + continue; + wstrToLower(wpname); + + if (!(wplayer_name.empty() || wpname.find(wplayer_name) != std::wstring::npos)) + continue; + + std::string gname = objmgr.GetGuildNameById(itr->second->GetGuildId()); + std::wstring wgname; + if(!Utf8toWStr(gname,wgname)) + continue; + wstrToLower(wgname); + + if (!(wguild_name.empty() || wgname.find(wguild_name) != std::wstring::npos)) + continue; + + std::string aname; + if(AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(itr->second->GetZoneId())) + aname = areaEntry->area_name[GetSessionDbcLocale()]; + + bool s_show = true; + for(uint32 i = 0; i < str_count; i++) + { + if (!str[i].empty()) + { + if (wgname.find(str[i]) != std::wstring::npos || + wpname.find(str[i]) != std::wstring::npos || + Utf8FitTo(aname, str[i]) ) + { + s_show = true; + break; + } + s_show = false; + } + } + if (!s_show) + continue; + + data << pname; // player name + data << gname; // guild name + data << uint32( lvl ); // player level + data << uint32( class_ ); // player class + data << uint32( race ); // player race + data << uint8(0); // new 2.4.0 + data << uint32( pzoneid ); // player zone id + + // 49 is maximum player count sent to client + if ((++clientcount) == 49) + break; + } + + data.put( 0, clientcount ); //insert right count + data.put( sizeof(uint32), clientcount ); //insert right count + + SendPacket(&data); + sLog.outDebug( "WORLD: Send SMSG_WHO Message" ); +} + +void WorldSession::HandleLogoutRequestOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug( "WORLD: Recvd CMSG_LOGOUT_REQUEST Message, security - %u", GetSecurity() ); + + if (uint64 lguid = GetPlayer()->GetLootGUID()) + DoLootRelease(lguid); + + //instant logout for admins, gm's, mod's + if( GetSecurity() > SEC_PLAYER ) + { + LogoutPlayer(true); + return; + } + + //Can not logout if... + if( GetPlayer()->isInCombat() || //...is in combat + GetPlayer()->duel || //...is in Duel + //...is jumping ...is falling + GetPlayer()->HasUnitMovementFlag(MOVEMENTFLAG_JUMPING | MOVEMENTFLAG_FALLING)) + { + WorldPacket data( SMSG_LOGOUT_RESPONSE, (2+4) ) ; + data << (uint8)0xC; + data << uint32(0); + data << uint8(0); + SendPacket( &data ); + LogoutRequest(0); + return; + } + + //instant logout in taverns/cities or on taxi + if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || GetPlayer()->isInFlight()) + { + LogoutPlayer(true); + return; + } + + // not set flags if player can't free move to prevent lost state at logout cancel + if(GetPlayer()->CanFreeMove()) + { + GetPlayer()->SetStandState(PLAYER_STATE_SIT); + + WorldPacket data( SMSG_FORCE_MOVE_ROOT, (8+4) ); // guess size + data.append(GetPlayer()->GetPackGUID()); + data << (uint32)2; + SendPacket( &data ); + GetPlayer()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + } + + WorldPacket data( SMSG_LOGOUT_RESPONSE, 5 ); + data << uint32(0); + data << uint8(0); + SendPacket( &data ); + LogoutRequest(time(NULL)); +} + +void WorldSession::HandlePlayerLogoutOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug( "WORLD: Recvd CMSG_PLAYER_LOGOUT Message" ); +} + +void WorldSession::HandleLogoutCancelOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug( "WORLD: Recvd CMSG_LOGOUT_CANCEL Message" ); + + LogoutRequest(0); + + WorldPacket data( SMSG_LOGOUT_CANCEL_ACK, 0 ); + SendPacket( &data ); + + // not remove flags if can't free move - its not set in Logout request code. + if(GetPlayer()->CanFreeMove()) + { + //!we can move again + data.Initialize( SMSG_FORCE_MOVE_UNROOT, 8 ); // guess size + data.append(GetPlayer()->GetPackGUID()); + data << uint32(0); + SendPacket( &data ); + + //! Stand Up + GetPlayer()->SetStandState(PLAYER_STATE_NONE); + + //! DISABLE_ROTATE + GetPlayer()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + } + + sLog.outDebug( "WORLD: sent SMSG_LOGOUT_CANCEL_ACK Message" ); +} + +void WorldSession::SendGMTicketGetTicket(uint32 status, char const* text) +{ + int len = text ? strlen(text) : 0; + WorldPacket data( SMSG_GMTICKET_GETTICKET, (4+len+1+4+2+4+4) ); + data << uint32(status); // standard 0x0A, 0x06 if text present + if(status == 6) + { + data << text; // ticket text + data << uint8(0x7); // ticket category + data << float(0); // time from ticket creation? + data << float(0); // const + data << float(0); // const + data << uint8(0); // const + data << uint8(0); // const + } + SendPacket( &data ); +} + +void WorldSession::HandleGMTicketGetTicketOpcode( WorldPacket & /*recv_data*/ ) +{ + WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 ); + data << (uint32)time(NULL); + data << (uint32)0; + SendPacket( &data ); + + uint64 guid; + Field *fields; + guid = GetPlayer()->GetGUID(); + + QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(ticket_id) FROM character_ticket WHERE guid = '%u'", GUID_LOPART(guid)); + + if (result) + { + int cnt; + fields = result->Fetch(); + cnt = fields[0].GetUInt32(); + delete result; + + if ( cnt > 0 ) + { + QueryResult *result2 = CharacterDatabase.PQuery("SELECT ticket_text FROM character_ticket WHERE guid = '%u'", GUID_LOPART(guid)); + if(result2) + { + Field *fields2 = result2->Fetch(); + SendGMTicketGetTicket(0x06,fields2[0].GetString()); + delete result2; + } + } + else + SendGMTicketGetTicket(0x0A,0); + } +} + +void WorldSession::HandleGMTicketUpdateTextOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,1); + + std::string ticketText; + recv_data >> ticketText; + + CharacterDatabase.escape_string(ticketText); + CharacterDatabase.PExecute("UPDATE character_ticket SET ticket_text = '%s' WHERE guid = '%u'", ticketText.c_str(), _player->GetGUIDLow()); +} + +void WorldSession::HandleGMTicketDeleteOpcode( WorldPacket & /*recv_data*/ ) +{ + uint32 guid = GetPlayer()->GetGUIDLow(); + + CharacterDatabase.PExecute("DELETE FROM character_ticket WHERE guid = '%u' LIMIT 1",guid); + + WorldPacket data( SMSG_GMTICKET_DELETETICKET, 4 ); + data << uint32(9); + SendPacket( &data ); + + SendGMTicketGetTicket(0x0A, 0); +} + +void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 4*4+1+2*4); + + uint32 map; + float x, y, z; + std::string ticketText = ""; + uint32 unk1, unk2; + + recv_data >> map >> x >> y >> z; // last check 2.4.3 + recv_data >> ticketText; + + // recheck + CHECK_PACKET_SIZE(recv_data,4*4+(ticketText.size()+1)+2*4); + + recv_data >> unk1 >> unk2; + // note: the packet might contain more data, but the exact structure of that is unknown + + sLog.outDebug("TicketCreate: map %u, x %f, y %f, z %f, text %s, unk1 %u, unk2 %u", map, x, y, z, ticketText.c_str(), unk1, unk2); + + CharacterDatabase.escape_string(ticketText); + + QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM character_ticket WHERE guid = '%u'", _player->GetGUIDLow()); + + if (result) + { + int cnt; + Field *fields = result->Fetch(); + cnt = fields[0].GetUInt32(); + delete result; + + if ( cnt > 0 ) + { + WorldPacket data( SMSG_GMTICKET_CREATE, 4 ); + data << uint32(1); + SendPacket( &data ); + } + else + { + CharacterDatabase.PExecute("INSERT INTO character_ticket (guid,ticket_text) VALUES ('%u', '%s')", _player->GetGUIDLow(), ticketText.c_str()); + + WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 ); + data << (uint32)time(NULL); + data << (uint32)0; + SendPacket( &data ); + + data.Initialize( SMSG_GMTICKET_CREATE, 4 ); + data << uint32(2); + SendPacket( &data ); + DEBUG_LOG("update the ticket\n"); + + //TODO: Guard player map + HashMapHolder::MapType &m = ObjectAccessor::Instance().GetPlayers(); + for(HashMapHolder::MapType::iterator itr = m.begin(); itr != m.end(); ++itr) + { + if(itr->second->GetSession()->GetSecurity() >= SEC_GAMEMASTER && itr->second->isAcceptTickets()) + ChatHandler(itr->second).PSendSysMessage(LANG_COMMAND_TICKETNEW,GetPlayer()->GetName()); + } + } + } +} + +void WorldSession::HandleGMTicketSystemStatusOpcode( WorldPacket & /*recv_data*/ ) +{ + WorldPacket data( SMSG_GMTICKET_SYSTEMSTATUS,4 ); + data << uint32(1); // we can also disable ticket system by sending 0 value + + SendPacket( &data ); +} + +void WorldSession::HandleGMSurveySubmit( WorldPacket & recv_data) +{ + // GM survey is shown after SMSG_GM_TICKET_STATUS_UPDATE with status = 3 + CHECK_PACKET_SIZE(recv_data,4+4); + uint32 x; + recv_data >> x; // answer range? (6 = 0-5?) + sLog.outDebug("SURVEY: X = %u", x); + + uint8 result[10]; + memset(result, 0, sizeof(result)); + for( int i = 0; i < 10; ++i) + { + CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+4); + uint32 questionID; + recv_data >> questionID; // GMSurveyQuestions.dbc + if (!questionID) + break; + + CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+1+1); + uint8 value; + std::string unk_text; + recv_data >> value; // answer + recv_data >> unk_text; // always empty? + + result[i] = value; + sLog.outDebug("SURVEY: ID %u, value %u, text %s", questionID, value, unk_text.c_str()); + } + + CHECK_PACKET_SIZE(recv_data,recv_data.rpos()+1); + std::string comment; + recv_data >> comment; // addional comment + sLog.outDebug("SURVEY: comment %s", comment.c_str()); + + // TODO: chart this data in some way +} + +void WorldSession::HandleTogglePvP( WorldPacket & recv_data ) +{ + // this opcode can be used in two ways: Either set explicit new status or toggle old status + if(recv_data.size() == 1) + { + bool newPvPStatus; + recv_data >> newPvPStatus; + GetPlayer()->ApplyModFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP, newPvPStatus); + } + else + { + GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP); + } + + if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP)) + { + if(!GetPlayer()->IsPvP() || GetPlayer()->pvpInfo.endTimer != 0) + GetPlayer()->UpdatePvP(true, true); + } + else + { + if(!GetPlayer()->pvpInfo.inHostileArea && GetPlayer()->IsPvP()) + GetPlayer()->pvpInfo.endTimer = time(NULL); // start toggle-off + } +} + +void WorldSession::HandleZoneUpdateOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4); + + uint32 newZone; + recv_data >> newZone; + + sLog.outDetail("WORLD: Recvd ZONE_UPDATE: %u", newZone); + + if(newZone != _player->GetZoneId()) + GetPlayer()->SendInitWorldStates(); // only if really enters to new zone, not just area change, works strange... + + GetPlayer()->UpdateZone(newZone); +} + +void WorldSession::HandleSetTargetOpcode( WorldPacket & recv_data ) +{ + // When this packet send? + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid ; + recv_data >> guid; + + _player->SetUInt32Value(UNIT_FIELD_TARGET,guid); + + // update reputation list if need + Unit* unit = ObjectAccessor::GetUnit(*_player, guid ); + if(!unit) + return; + + _player->SetFactionVisibleForFactionTemplateId(unit->getFaction()); +} + +void WorldSession::HandleSetSelectionOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid; + recv_data >> guid; + + _player->SetSelection(guid); + + // update reputation list if need + Unit* unit = ObjectAccessor::GetUnit(*_player, guid ); + if(!unit) + return; + + _player->SetFactionVisibleForFactionTemplateId(unit->getFaction()); +} + +void WorldSession::HandleStandStateChangeOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,1); + + sLog.outDebug( "WORLD: Received CMSG_STAND_STATE_CHANGE" ); + uint8 animstate; + recv_data >> animstate; + + _player->SetStandState(animstate); +} + +void WorldSession::HandleFriendListOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 4); + sLog.outDebug( "WORLD: Received CMSG_CONTACT_LIST" ); + uint32 unk; + recv_data >> unk; + sLog.outDebug("unk value is %u", unk); + _player->GetSocial()->SendSocialList(); +} + +void WorldSession::HandleAddFriendOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 1+1); + + sLog.outDebug( "WORLD: Received CMSG_ADD_FRIEND" ); + + std::string friendName = GetMangosString(LANG_FRIEND_IGNORE_UNKNOWN); + std::string friendNote; + FriendsResult friendResult = FRIEND_NOT_FOUND; + Player *pFriend = NULL; + uint64 friendGuid = 0; + + recv_data >> friendName; + + // recheck + CHECK_PACKET_SIZE(recv_data, (friendName.size()+1)+1); + + recv_data >> friendNote; + + if(!normalizePlayerName(friendName)) + return; + + CharacterDatabase.escape_string(friendName); // prevent SQL injection - normal name don't must changed by this call + + sLog.outDebug( "WORLD: %s asked to add friend : '%s'", + GetPlayer()->GetName(), friendName.c_str() ); + + friendGuid = objmgr.GetPlayerGUIDByName(friendName); + + if(friendGuid) + { + pFriend = ObjectAccessor::FindPlayer(friendGuid); + if(pFriend==GetPlayer()) + friendResult = FRIEND_SELF; + else if(GetPlayer()->GetTeam()!=objmgr.GetPlayerTeamByGUID(friendGuid) && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND) && GetSecurity() < SEC_MODERATOR) + friendResult = FRIEND_ENEMY; + else if(GetPlayer()->GetSocial()->HasFriend(GUID_LOPART(friendGuid))) + friendResult = FRIEND_ALREADY; + } + + if (friendGuid && friendResult==FRIEND_NOT_FOUND) + { + if( pFriend && pFriend->IsInWorld() && pFriend->IsVisibleGloballyFor(GetPlayer())) + friendResult = FRIEND_ADDED_ONLINE; + else + friendResult = FRIEND_ADDED_OFFLINE; + + if(!_player->GetSocial()->AddToSocialList(GUID_LOPART(friendGuid), false)) + { + friendResult = FRIEND_LIST_FULL; + sLog.outDebug( "WORLD: %s's friend list is full.", GetPlayer()->GetName()); + } + + _player->GetSocial()->SetFriendNote(GUID_LOPART(friendGuid), friendNote); + + sLog.outDebug( "WORLD: %s Guid found '%u'.", friendName.c_str(), GUID_LOPART(friendGuid)); + } + else if(friendResult==FRIEND_ALREADY) + { + sLog.outDebug( "WORLD: %s Guid Already a Friend.", friendName.c_str() ); + } + else if(friendResult==FRIEND_SELF) + { + sLog.outDebug( "WORLD: %s Guid can't add himself.", friendName.c_str() ); + } + else + { + sLog.outDebug( "WORLD: %s Guid not found.", friendName.c_str() ); + } + + sSocialMgr.SendFriendStatus(GetPlayer(), friendResult, GUID_LOPART(friendGuid), friendName, false); + + sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" ); +} + +void WorldSession::HandleDelFriendOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + uint64 FriendGUID; + + sLog.outDebug( "WORLD: Received CMSG_DEL_FRIEND" ); + + recv_data >> FriendGUID; + + _player->GetSocial()->RemoveFromSocialList(GUID_LOPART(FriendGUID), false); + + sSocialMgr.SendFriendStatus(GetPlayer(), FRIEND_REMOVED, GUID_LOPART(FriendGUID), "", false); + + sLog.outDebug( "WORLD: Sent motd (SMSG_FRIEND_STATUS)" ); +} + +void WorldSession::HandleAddIgnoreOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,1); + + sLog.outDebug( "WORLD: Received CMSG_ADD_IGNORE" ); + + std::string IgnoreName = GetMangosString(LANG_FRIEND_IGNORE_UNKNOWN); + FriendsResult ignoreResult = FRIEND_IGNORE_NOT_FOUND; + uint64 IgnoreGuid = 0; + + recv_data >> IgnoreName; + + if(!normalizePlayerName(IgnoreName)) + return; + + CharacterDatabase.escape_string(IgnoreName); // prevent SQL injection - normal name don't must changed by this call + + sLog.outDebug( "WORLD: %s asked to Ignore: '%s'", + GetPlayer()->GetName(), IgnoreName.c_str() ); + + IgnoreGuid = objmgr.GetPlayerGUIDByName(IgnoreName); + + if(IgnoreGuid) + { + if(IgnoreGuid==GetPlayer()->GetGUID()) + ignoreResult = FRIEND_IGNORE_SELF; + else + { + if( GetPlayer()->GetSocial()->HasIgnore(GUID_LOPART(IgnoreGuid)) ) + ignoreResult = FRIEND_IGNORE_ALREADY; + } + } + + if (IgnoreGuid && ignoreResult == FRIEND_IGNORE_NOT_FOUND) + { + ignoreResult = FRIEND_IGNORE_ADDED; + + _player->GetSocial()->AddToSocialList(GUID_LOPART(IgnoreGuid), true); + } + else if(ignoreResult==FRIEND_IGNORE_ALREADY) + { + sLog.outDebug( "WORLD: %s Guid Already Ignored.", IgnoreName.c_str() ); + } + else if(ignoreResult==FRIEND_IGNORE_SELF) + { + sLog.outDebug( "WORLD: %s Guid can't add himself.", IgnoreName.c_str() ); + } + else + { + sLog.outDebug( "WORLD: %s Guid not found.", IgnoreName.c_str() ); + } + + sSocialMgr.SendFriendStatus(GetPlayer(), ignoreResult, GUID_LOPART(IgnoreGuid), "", false); + + sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" ); +} + +void WorldSession::HandleDelIgnoreOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + uint64 IgnoreGUID; + + sLog.outDebug( "WORLD: Received CMSG_DEL_IGNORE" ); + + recv_data >> IgnoreGUID; + + _player->GetSocial()->RemoveFromSocialList(GUID_LOPART(IgnoreGUID), true); + + sSocialMgr.SendFriendStatus(GetPlayer(), FRIEND_IGNORE_REMOVED, GUID_LOPART(IgnoreGUID), "", false); + + sLog.outDebug( "WORLD: Sent motd (SMSG_FRIEND_STATUS)" ); +} + +void WorldSession::HandleSetFriendNoteOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 8+1); + uint64 guid; + std::string note; + recv_data >> guid >> note; + _player->GetSocial()->SetFriendNote(guid, note); +} + +void WorldSession::HandleBugOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+4+1+4+1); + + uint32 suggestion, contentlen; + std::string content; + uint32 typelen; + std::string type; + + recv_data >> suggestion >> contentlen >> content; + + //recheck + CHECK_PACKET_SIZE(recv_data,4+4+(content.size()+1)+4+1); + + recv_data >> typelen >> type; + + if( suggestion == 0 ) + sLog.outDebug( "WORLD: Received CMSG_BUG [Bug Report]" ); + else + sLog.outDebug( "WORLD: Received CMSG_BUG [Suggestion]" ); + + sLog.outDebug( type.c_str( ) ); + sLog.outDebug( content.c_str( ) ); + + CharacterDatabase.escape_string(type); + CharacterDatabase.escape_string(content); + CharacterDatabase.PExecute ("INSERT INTO bugreport (type,content) VALUES('%s', '%s')", type.c_str( ), content.c_str( )); +} + +void WorldSession::HandleCorpseReclaimOpcode(WorldPacket &recv_data) +{ + CHECK_PACKET_SIZE(recv_data,8); + + sLog.outDetail("WORLD: Received CMSG_RECLAIM_CORPSE"); + if (GetPlayer()->isAlive()) + return; + + // 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(guid_size + 4 + curtalent_rank_slot); + val |= (1 << curtalent_rank_offset); + data.put(guid_size + 4 + curtalent_rank_slot, val & 0xFF); + } + + talentTabPos += GetTalentTabInspectBitSize(talentTabId); + } + } + + SendPacket(&data); +} + +void WorldSession::HandleInspectHonorStatsOpcode(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + uint64 guid; + recv_data >> guid; + + Player *player = objmgr.GetPlayer(guid); + + if(!player) + { + sLog.outError("InspectHonorStats: WTF, player not found..."); + return; + } + + WorldPacket data(MSG_INSPECT_HONOR_STATS, 8+1+4*4); + data << uint64(player->GetGUID()); + data << uint8(player->GetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY)); + data << uint32(player->GetUInt32Value(PLAYER_FIELD_KILLS)); + data << uint32(player->GetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION)); + data << uint32(player->GetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION)); + data << uint32(player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORBALE_KILLS)); + SendPacket(&data); +} + +void WorldSession::HandleWorldTeleportOpcode(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data,4+4+4+4+4+4); + + // write in client console: worldport 469 452 6454 2536 180 or /console worldport 469 452 6454 2536 180 + // Received opcode CMSG_WORLD_TELEPORT + // Time is ***, map=469, x=452.000000, y=6454.000000, z=2536.000000, orient=3.141593 + + //sLog.outDebug("Received opcode CMSG_WORLD_TELEPORT"); + + if(GetPlayer()->isInFlight()) + { + sLog.outDebug("Player '%s' (GUID: %u) in flight, ignore worldport command.",GetPlayer()->GetName(),GetPlayer()->GetGUIDLow()); + return; + } + + uint32 time; + uint32 mapid; + float PositionX; + float PositionY; + float PositionZ; + float Orientation; + + recv_data >> time; // time in m.sec. + recv_data >> mapid; + recv_data >> PositionX; + recv_data >> PositionY; + recv_data >> PositionZ; + recv_data >> Orientation; // o (3.141593 = 180 degrees) + DEBUG_LOG("Time %u sec, map=%u, x=%f, y=%f, z=%f, orient=%f", time/1000, mapid, PositionX, PositionY, PositionZ, Orientation); + + if (GetSecurity() >= SEC_ADMINISTRATOR) + GetPlayer()->TeleportTo(mapid,PositionX,PositionY,PositionZ,Orientation); + else + SendNotification("You do not have permission to perform that function"); + sLog.outDebug("Received worldport command from player %s", GetPlayer()->GetName()); +} + +void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 1); + + sLog.outDebug("Received opcode CMSG_WHOIS"); + std::string charname, acc, email, lastip, msg; + recv_data >> charname; + + if (GetSecurity() < SEC_ADMINISTRATOR) + { + SendNotification("You do not have permission to perform that function"); + return; + } + + if(charname.empty()) + { + SendNotification("Please provide character name"); + return; + } + + uint32 accid; + Field *fields; + + Player *plr = objmgr.GetPlayer(charname.c_str()); + + if(plr) + accid = plr->GetSession()->GetAccountId(); + else + { + SendNotification("Player %s not found or offline", charname.c_str()); + return; + } + + if(!accid) + { + SendNotification("Account for character %s not found", charname.c_str()); + return; + } + + QueryResult *result = loginDatabase.PQuery("SELECT username,email,last_ip FROM account WHERE id=%u", accid); + if(result) + { + fields = result->Fetch(); + acc = fields[0].GetCppString(); + if(acc.empty()) + acc = "Unknown"; + email = fields[1].GetCppString(); + if(email.empty()) + email = "Unknown"; + lastip = fields[2].GetCppString(); + if(lastip.empty()) + lastip = "Unknown"; + msg = charname + "'s " + "account is " + acc + ", e-mail: " + email + ", last ip: " + lastip; + + WorldPacket data(SMSG_WHOIS, msg.size()+1); + data << msg; + _player->GetSession()->SendPacket(&data); + } + else + SendNotification("Account for character %s not found", charname.c_str()); + + delete result; + sLog.outDebug("Received whois command from player %s for character %s", GetPlayer()->GetName(), charname.c_str()); +} + +void WorldSession::HandleReportSpamOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 1+8); + sLog.outDebug("WORLD: CMSG_REPORT_SPAM"); + recv_data.hexlike(); + + uint8 spam_type; // 0 - mail, 1 - chat + uint64 spammer_guid; + uint32 unk1, unk2, unk3, unk4 = 0; + std::string description = ""; + recv_data >> spam_type; // unk 0x01 const, may be spam type (mail/chat) + recv_data >> spammer_guid; // player guid + switch(spam_type) + { + case 0: + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4); + recv_data >> unk1; // const 0 + recv_data >> unk2; // probably mail id + recv_data >> unk3; // const 0 + break; + case 1: + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4+1); + recv_data >> unk1; // probably language + recv_data >> unk2; // message type? + recv_data >> unk3; // probably channel id + recv_data >> unk4; // unk random value + recv_data >> description; // spam description string (messagetype, channel name, player name, message) + break; + } + + // NOTE: all chat messages from this spammer automatically ignored by spam reporter until logout in case chat spam. + // if it's mail spam - ALL mails from this spammer automatically removed by client + + // Complaint Received message + WorldPacket data(SMSG_COMPLAIN_RESULT, 1); + data << uint8(0); + SendPacket(&data); + + sLog.outDebug("REPORT SPAM: type %u, guid %u, unk1 %u, unk2 %u, unk3 %u, unk4 %u, message %s", spam_type, GUID_LOPART(spammer_guid), unk1, unk2, unk3, unk4, description.c_str()); +} + +void WorldSession::HandleRealmStateRequestOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 4); + + sLog.outDebug("CMSG_REALM_SPLIT"); + + uint32 unk; + std::string split_date = "01/01/01"; + recv_data >> unk; + + WorldPacket data(SMSG_REALM_SPLIT, 4+4+split_date.size()+1); + data << unk; + data << uint32(0x00000000); // realm split state + // split states: + // 0x0 realm normal + // 0x1 realm split + // 0x2 realm split pending + data << split_date; + SendPacket(&data); + //sLog.outDebug("response sent %u", unk); +} + +void WorldSession::HandleFarSightOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 1); + + sLog.outDebug("WORLD: CMSG_FAR_SIGHT"); + //recv_data.hexlike(); + + uint8 unk; + recv_data >> unk; + + switch(unk) + { + case 0: + //WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0) + //SendPacket(&data); + //_player->SetUInt64Value(PLAYER_FARSIGHT, 0); + sLog.outDebug("Removed FarSight from player %u", _player->GetGUIDLow()); + break; + case 1: + sLog.outDebug("Added FarSight " I64FMTD " to player %u", _player->GetUInt64Value(PLAYER_FARSIGHT), _player->GetGUIDLow()); + break; + } +} + +void WorldSession::HandleChooseTitleOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 4); + + sLog.outDebug("CMSG_SET_TITLE"); + + int32 title; + recv_data >> title; + + // -1 at none + if(title > 0 && title < 64) + { + if(!GetPlayer()->HasFlag64(PLAYER__FIELD_KNOWN_TITLES,uint64(1) << title)) + return; + } + else + title = 0; + + GetPlayer()->SetUInt32Value(PLAYER_CHOSEN_TITLE, title); +} + +void WorldSession::HandleAllowMoveAckOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 4+4); + + sLog.outDebug("CMSG_ALLOW_MOVE_ACK"); + + uint32 counter, time_; + recv_data >> counter >> time_; + + // time_ seems always more than getMSTime() + uint32 diff = getMSTimeDiff(getMSTime(),time_); + + sLog.outDebug("response sent: counter %u, time %u (HEX: %X), ms. time %u, diff %u", counter, time_, time_, getMSTime(), diff); +} + +void WorldSession::HandleResetInstancesOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug("WORLD: CMSG_RESET_INSTANCES"); + Group *pGroup = _player->GetGroup(); + if(pGroup) + { + if(pGroup->IsLeader(_player->GetGUID())) + pGroup->ResetInstances(INSTANCE_RESET_ALL, _player); + } + else + _player->ResetInstances(INSTANCE_RESET_ALL); +} + +void WorldSession::HandleDungeonDifficultyOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 4); + + sLog.outDebug("MSG_SET_DUNGEON_DIFFICULTY"); + + uint32 mode; + recv_data >> mode; + + if(mode == _player->GetDifficulty()) + return; + + if(mode > DIFFICULTY_HEROIC) + { + sLog.outError("WorldSession::HandleDungeonDifficultyOpcode: player %d sent an invalid instance mode %d!", _player->GetGUIDLow(), mode); + return; + } + + // cannot reset while in an instance + Map *map = _player->GetMap(); + if(map && map->IsDungeon()) + { + sLog.outError("WorldSession::HandleDungeonDifficultyOpcode: player %d tried to reset the instance while inside!", _player->GetGUIDLow()); + return; + } + + if(_player->getLevel() < LEVELREQUIREMENT_HEROIC) + return; + Group *pGroup = _player->GetGroup(); + if(pGroup) + { + if(pGroup->IsLeader(_player->GetGUID())) + { + // the difficulty is set even if the instances can't be reset + //_player->SendDungeonDifficulty(true); + pGroup->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, _player); + pGroup->SetDifficulty(mode); + } + } + else + { + _player->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY); + _player->SetDifficulty(mode); + } +} + +void WorldSession::HandleNewUnknownOpcode( WorldPacket & recv_data ) +{ + sLog.outDebug("New Unknown Opcode %u", recv_data.GetOpcode()); + recv_data.hexlike(); + /* + New Unknown Opcode 837 + STORAGE_SIZE: 60 + 02 00 00 00 00 00 00 00 | 00 00 00 00 01 20 00 00 + 89 EB 33 01 71 5C 24 C4 | 15 03 35 45 74 47 8B 42 + BA B8 1B 40 00 00 00 00 | 00 00 00 00 77 66 42 BF + 23 91 26 3F 00 00 60 41 | 00 00 00 00 + + New Unknown Opcode 837 + STORAGE_SIZE: 44 + 02 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00 + 7B 80 34 01 84 EA 2B C4 | 5F A1 36 45 C9 39 1C 42 + BA B8 1B 40 CE 06 00 00 | 00 00 80 3F + */ +} + +void WorldSession::HandleDismountOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug("WORLD: CMSG_CANCEL_MOUNT_AURA"); + //recv_data.hexlike(); + + //If player is not mounted, so go out :) + if (!_player->IsMounted()) // not blizz like; no any messages on blizz + { + ChatHandler(this).SendSysMessage(LANG_CHAR_NON_MOUNTED); + return; + } + + if(_player->isInFlight()) // not blizz like; no any messages on blizz + { + ChatHandler(this).SendSysMessage(LANG_YOU_IN_FLIGHT); + return; + } + + _player->Unmount(); + _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); +} + +void WorldSession::HandleMoveFlyModeChangeAckOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 8+4+4); + + // fly mode on/off + sLog.outDebug("WORLD: CMSG_MOVE_SET_CAN_FLY_ACK"); + //recv_data.hexlike(); + + uint64 guid; + uint32 unk; + uint32 flags; + + recv_data >> guid >> unk >> flags; + + _player->SetUnitMovementFlags(flags); + /* + on: + 25 00 00 00 00 00 00 00 | 00 00 00 00 00 00 80 00 + 85 4E A9 01 19 BA 7A C3 | 42 0D 70 44 44 B0 A8 42 + 78 15 94 40 39 03 00 00 | 00 00 80 3F + off: + 25 00 00 00 00 00 00 00 | 00 00 00 00 00 00 00 00 + 10 FD A9 01 19 BA 7A C3 | 42 0D 70 44 44 B0 A8 42 + 78 15 94 40 39 03 00 00 | 00 00 00 00 + */ +} + +void WorldSession::HandleRequestPetInfoOpcode( WorldPacket & /*recv_data */) +{ + /* + sLog.outDebug("WORLD: CMSG_REQUEST_PET_INFO"); + recv_data.hexlike(); + */ +} + +void WorldSession::HandleSetTaxiBenchmarkOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 1); + + uint8 mode; + recv_data >> mode; + + sLog.outDebug("Client used \"/timetest %d\" command", mode); +} diff --git a/src/game/MotionMaster.cpp b/src/game/MotionMaster.cpp new file mode 100644 index 00000000000..cf910bf50b2 --- /dev/null +++ b/src/game/MotionMaster.cpp @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 + +inline bool isStatic(MovementGenerator *mv) +{ + return (mv == &si_idleMovement); +} + +void +MotionMaster::Initialize() +{ + // clear ALL movement generators (including default) + while(!empty()) + { + MovementGenerator *curr = top(); + curr->Finalize(*i_owner); + pop(); + if( !isStatic( curr ) ) + delete curr; + } + + // set new default movement generator + if(i_owner->GetTypeId() == TYPEID_UNIT) + { + MovementGenerator* movement = FactorySelector::selectMovementGenerator((Creature*)i_owner); + push( movement == NULL ? &si_idleMovement : movement ); + top()->Initialize(*i_owner); + } + else + push(&si_idleMovement); +} + +MotionMaster::~MotionMaster() +{ + // clear ALL movement generators (including default) + while(!empty()) + { + MovementGenerator *curr = top(); + curr->Finalize(*i_owner); + pop(); + if( !isStatic( curr ) ) + delete curr; + } +} + +void +MotionMaster::UpdateMotion(const uint32 &diff) +{ + if( i_owner->hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED) ) + return; + assert( !empty() ); + if (!top()->Update(*i_owner, diff)) + MovementExpired(); +} + +void +MotionMaster::Clear(bool reset) +{ + while( !empty() && size() > 1 ) + { + MovementGenerator *curr = top(); + curr->Finalize(*i_owner); + pop(); + if( !isStatic( curr ) ) + delete curr; + } + + if (reset) + { + assert( !empty() ); + top()->Reset(*i_owner); + } +} + +void +MotionMaster::MovementExpired(bool reset) +{ + if( empty() || size() == 1 ) + return; + + MovementGenerator *curr = top(); + curr->Finalize(*i_owner); + pop(); + + if( !isStatic(curr) ) + delete curr; + + assert( !empty() ); + while( !empty() && top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE ) + { + // Should check if target is still valid? If not valid it will crash. + curr = top(); + curr->Finalize(*i_owner); + pop(); + delete curr; + } + if( empty() ) + Initialize(); + if (reset) top()->Reset(*i_owner); +} + +void MotionMaster::MoveIdle() +{ + if( empty() || !isStatic( top() ) ) + push( &si_idleMovement ); +} + +void +MotionMaster::MoveTargetedHome() +{ + if(i_owner->hasUnitState(UNIT_STAT_FLEEING)) + return; + + Clear(false); + + if(i_owner->GetTypeId()==TYPEID_UNIT && !((Creature*)i_owner)->GetCharmerOrOwnerGUID()) + { + DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted home", i_owner->GetEntry(), i_owner->GetGUIDLow()); + Mutate(new HomeMovementGenerator()); + } + 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()); + } + else + { + DEBUG_LOG("Creature (Entry: %u GUID: %u) move confused", + i_owner->GetEntry(), i_owner->GetGUIDLow() ); + Mutate(new ConfusedMovementGenerator()); + } +} + +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(*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(*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(*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(*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(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(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(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(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/MotionMaster.h b/src/game/MotionMaster.h new file mode 100644 index 00000000000..f7a38e47618 --- /dev/null +++ b/src/game/MotionMaster.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_MOTIONMASTER_H +#define MANGOS_MOTIONMASTER_H + +#include "Common.h" +#include + +class MovementGenerator; +class Unit; + +// Creature Entry ID used for waypoints show, visible only for GMs +#define VISUAL_WAYPOINT 1 + +// values 0 ... MAX_DB_MOTION_TYPE-1 used in DB +enum MovementGeneratorType +{ + IDLE_MOTION_TYPE = 0, // IdleMovementGenerator.h + RANDOM_MOTION_TYPE = 1, // RandomMovementGenerator.h + WAYPOINT_MOTION_TYPE = 2, // WaypointMovementGenerator.h + MAX_DB_MOTION_TYPE = 3, // *** this and below motion types can't be set in DB. + ANIMAL_RANDOM_MOTION_TYPE = MAX_DB_MOTION_TYPE, // AnimalRandomMovementGenerator.h + CONFUSED_MOTION_TYPE = 4, // ConfusedMovementGenerator.h + TARGETED_MOTION_TYPE = 5, // TargetedMovementGenerator.h + HOME_MOTION_TYPE = 6, // HomeMovementGenerator.h + FLIGHT_MOTION_TYPE = 7, // WaypointMovementGenerator.h + POINT_MOTION_TYPE = 8, // PointMovementGenerator.h + FLEEING_MOTION_TYPE = 9, // FleeingMovementGenerator.h + DISTRACT_MOTION_TYPE = 10, // IdleMovementGenerator.h +}; + +class MANGOS_DLL_SPEC MotionMaster : private std::stack +{ + private: + typedef std::stack Impl; + public: + + explicit MotionMaster(Unit *unit) : i_owner(unit) {} + ~MotionMaster(); + + void Initialize(); + + MovementGenerator* operator->(void) { return top(); } + + using Impl::top; + using Impl::empty; + + typedef Impl::container_type::const_iterator const_iterator; + const_iterator begin() const { return Impl::c.begin(); } + const_iterator end() const { return Impl::c.end(); } + + void UpdateMotion(const uint32 &diff); + void Clear(bool reset = true); + void MovementExpired(bool reset = true); + + void MoveIdle(); + void MoveTargetedHome(); + void MoveFollow(Unit* target, float dist, float angle); + void MoveChase(Unit* target, float dist = 0.0f, float angle = 0.0f); + void MoveConfused(); + void MoveFleeing(Unit* enemy); + void MovePoint(uint32 id, float x,float y,float z); + void MoveTaxiFlight(uint32 path, uint32 pathnode); + void MoveDistract(uint32 time); + + MovementGeneratorType GetCurrentMovementGeneratorType() const; + + void propagateSpeedChange(); + + bool GetDestination(float &x, float &y, float &z); + private: + void Mutate(MovementGenerator *m); // use Move* functions instead + + Unit *i_owner; +}; +#endif diff --git a/src/game/MovementGenerator.cpp b/src/game/MovementGenerator.cpp new file mode 100644 index 00000000000..b717f6cf443 --- /dev/null +++ b/src/game/MovementGenerator.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "MovementGenerator.h" + +MovementGenerator::~MovementGenerator() +{ +} diff --git a/src/game/MovementGenerator.h b/src/game/MovementGenerator.h new file mode 100644 index 00000000000..982de574c19 --- /dev/null +++ b/src/game/MovementGenerator.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_MOVEMENTGENERATOR_H +#define MANGOS_MOVEMENTGENERATOR_H + +#include "Platform/Define.h" +#include "Policies/Singleton.h" +#include "Dynamic/ObjectRegistry.h" +#include "Dynamic/FactoryHolder.h" +#include "Common.h" +#include "MotionMaster.h" + +class Unit; + +class MANGOS_DLL_SPEC MovementGenerator +{ + public: + virtual ~MovementGenerator(); + + virtual void Initialize(Unit &) = 0; + virtual void Finalize(Unit &) = 0; + + virtual void Reset(Unit &) = 0; + + virtual bool Update(Unit &, const uint32 &time_diff) = 0; + + virtual MovementGeneratorType GetMovementGeneratorType() = 0; + + virtual void unitSpeedChanged() { } + + virtual bool GetDestination(float& /*x*/, float& /*y*/, float& /*z*/) const { return false; } +}; + +template +class MANGOS_DLL_SPEC MovementGeneratorMedium : public MovementGenerator +{ + public: + void Initialize(Unit &u) + { + //u->AssertIsType(); + (static_cast(this))->Initialize(*((T*)&u)); + } + void Finalize(Unit &u) + { + //u->AssertIsType(); + (static_cast(this))->Finalize(*((T*)&u)); + } + void Reset(Unit &u) + { + //u->AssertIsType(); + (static_cast(this))->Reset(*((T*)&u)); + } + bool Update(Unit &u, const uint32 &time_diff) + { + //u->AssertIsType(); + return (static_cast(this))->Update(*((T*)&u), time_diff); + } + public: + // will not link if not overridden in the generators + void Initialize(T &u); + void Finalize(T &u); + void Reset(T &u); + bool Update(T &u, const uint32 &time_diff); +}; + +struct SelectableMovement : public FactoryHolder +{ + SelectableMovement(MovementGeneratorType mgt) : FactoryHolder(mgt) {} +}; + +template +struct MovementGeneratorFactory : public SelectableMovement +{ + MovementGeneratorFactory(MovementGeneratorType mgt) : SelectableMovement(mgt) {} + + MovementGenerator* Create(void *) const; +}; + +typedef FactoryHolder MovementGeneratorCreator; +typedef FactoryHolder::FactoryHolderRegistry MovementGeneratorRegistry; +typedef FactoryHolder::FactoryHolderRepository MovementGeneratorRepository; +#endif diff --git a/src/game/MovementGeneratorImpl.h b/src/game/MovementGeneratorImpl.h new file mode 100644 index 00000000000..c3859a548f5 --- /dev/null +++ b/src/game/MovementGeneratorImpl.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_MOVEMENTGENERATOR_IMPL_H +#define MANGOS_MOVEMENTGENERATOR_IMPL_H + +#include "MovementGenerator.h" + +template +inline MovementGenerator* +MovementGeneratorFactory::Create(void *data) const +{ + Creature* creature = reinterpret_cast(data); + return (new MOVEMENT_GEN(*creature)); +} +#endif diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp new file mode 100644 index 00000000000..1efd4e3c3e7 --- /dev/null +++ b/src/game/MovementHandler.cpp @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Opcodes.h" +#include "Log.h" +#include "World.h" +#include "Corpse.h" +#include "Player.h" +#include "MapManager.h" +#include "Transports.h" +#include "BattleGround.h" +#include "WaypointMovementGenerator.h" +#include "InstanceSaveMgr.h" + +void WorldSession::HandleMoveWorldportAckOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug( "WORLD: got MSG_MOVE_WORLDPORT_ACK." ); + HandleMoveWorldportAckOpcode(); +} + +void WorldSession::HandleMoveWorldportAckOpcode() +{ + // get the teleport destination + WorldLocation &loc = GetPlayer()->GetTeleportDest(); + + // possible errors in the coordinate validity check + if(!MapManager::IsValidMapCoord(loc.mapid,loc.x,loc.y,loc.z,loc.o)) + { + LogoutPlayer(false); + return; + } + + // get the destination map entry, not the current one, this will fix homebind and reset greeting + MapEntry const* mEntry = sMapStore.LookupEntry(loc.mapid); + InstanceTemplate const* mInstance = objmgr.GetInstanceTemplate(loc.mapid); + + // reset instance validity, except if going to an instance inside an instance + if(GetPlayer()->m_InstanceValid == false && !mInstance) + GetPlayer()->m_InstanceValid = true; + + GetPlayer()->SetSemaphoreTeleport(false); + + // relocate the player to the teleport destination + GetPlayer()->SetMapId(loc.mapid); + GetPlayer()->Relocate(loc.x, loc.y, loc.z, loc.o); + + // since the MapId is set before the GetInstance call, the InstanceId must be set to 0 + // to let GetInstance() determine the proper InstanceId based on the player's binds + GetPlayer()->SetInstanceId(0); + + // check this before Map::Add(player), because that will create the instance save! + bool reset_notify = (GetPlayer()->GetBoundInstance(GetPlayer()->GetMapId(), GetPlayer()->GetDifficulty()) == NULL); + + GetPlayer()->SendInitialPacketsBeforeAddToMap(); + // the CanEnter checks are done in TeleporTo but conditions may change + // while the player is in transit, for example the map may get full + if(!MapManager::Instance().GetMap(GetPlayer()->GetMapId(), GetPlayer())->Add(GetPlayer())) + { + sLog.outDebug("WORLD: teleport of player %s (%d) to location %d,%f,%f,%f,%f failed", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), loc.mapid, loc.x, loc.y, loc.z, loc.o); + // teleport the player home + GetPlayer()->SetDontMove(false); + if(!GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation())) + { + // the player must always be able to teleport home + sLog.outError("WORLD: failed to teleport player %s (%d) to homebind location %d,%f,%f,%f,%f!", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation()); + assert(false); + } + return; + } + GetPlayer()->SendInitialPacketsAfterAddToMap(); + + // flight fast teleport case + if(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()==FLIGHT_MOTION_TYPE) + { + if(!_player->InBattleGround()) + { + // short preparations to continue flight + GetPlayer()->SetDontMove(false); + FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top()); + flight->Initialize(*GetPlayer()); + return; + } + + // battleground state prepare, stop flight + GetPlayer()->GetMotionMaster()->MovementExpired(); + GetPlayer()->m_taxi.ClearTaxiDestinations(); + } + + // resurrect character at enter into instance where his corpse exist after add to map + Corpse *corpse = GetPlayer()->GetCorpse(); + if (corpse && corpse->GetType() != CORPSE_BONES && corpse->GetMapId() == GetPlayer()->GetMapId()) + { + if( mEntry->IsDungeon() ) + { + GetPlayer()->ResurrectPlayer(0.5f,false); + GetPlayer()->SpawnCorpseBones(); + GetPlayer()->SaveToDB(); + } + } + + if(mEntry->IsRaid() && mInstance) + { + if(reset_notify) + { + uint32 timeleft = sInstanceSaveManager.GetResetTimeFor(GetPlayer()->GetMapId()) - time(NULL); + GetPlayer()->SendInstanceResetWarning(GetPlayer()->GetMapId(), timeleft); // greeting at the entrance of the resort raid instance + } + } + + // mount allow check + if(!mEntry->IsMountAllowed()) + _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + + // battleground state preper + if(_player->InBattleGround()) + { + BattleGround *bg = _player->GetBattleGround(); + if(bg) + { + if(bg->GetMapId() == _player->GetMapId()) // we teleported to bg + { + if(!bg->GetBgRaid(_player->GetTeam())) // first player joined + { + Group *group = new Group; + bg->SetBgRaid(_player->GetTeam(), group); + group->Create(_player->GetGUIDLow(), _player->GetName()); + } + else // raid already exist + { + bg->GetBgRaid(_player->GetTeam())->AddMember(_player->GetGUID(), _player->GetName()); + } + } + } + } + + // honorless target + if(GetPlayer()->pvpInfo.inHostileArea) + GetPlayer()->CastSpell(GetPlayer(), 2479, true); + + // resummon pet + if(GetPlayer()->m_temporaryUnsummonedPetNumber) + { + Pet* NewPet = new Pet; + if(!NewPet->LoadPetFromDB(GetPlayer(), 0, GetPlayer()->m_temporaryUnsummonedPetNumber, true)) + delete NewPet; + + GetPlayer()->m_temporaryUnsummonedPetNumber = 0; + } + + GetPlayer()->SetDontMove(false); +} + +void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data, 4+1+4+4+4+4+4); + + if(GetPlayer()->GetDontMove()) + return; + + /* extract packet */ + MovementInfo movementInfo; + uint32 MovementFlags; + + recv_data >> MovementFlags; + recv_data >> movementInfo.unk1; + recv_data >> movementInfo.time; + recv_data >> movementInfo.x; + recv_data >> movementInfo.y; + recv_data >> movementInfo.z; + recv_data >> movementInfo.o; + + //Save movement flags + _player->SetUnitMovementFlags(MovementFlags); + + if(MovementFlags & MOVEMENTFLAG_ONTRANSPORT) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+8+4+4+4+4+4); + + recv_data >> movementInfo.t_guid; + recv_data >> movementInfo.t_x; + recv_data >> movementInfo.t_y; + recv_data >> movementInfo.t_z; + recv_data >> movementInfo.t_o; + recv_data >> movementInfo.t_time; + } + + if(MovementFlags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2)) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + + recv_data >> movementInfo.s_pitch; // pitch, -1.55=looking down, 0=looking straight forward, +1.55=looking up + } + + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + + recv_data >> movementInfo.fallTime; // duration of last jump (when in jump duration from jump begin to now) + + if(MovementFlags & MOVEMENTFLAG_JUMPING) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4); + + recv_data >> movementInfo.j_unk; // constant, but different when jumping in water and on land? + recv_data >> movementInfo.j_sinAngle; // sin of angle between orientation0 and players orientation + recv_data >> movementInfo.j_cosAngle; // cos of angle between orientation0 and players orientation + recv_data >> movementInfo.j_xyspeed; // speed of xy movement + } + + if(MovementFlags & MOVEMENTFLAG_SPLINE) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + + recv_data >> movementInfo.u_unk1; // unknown + } + /*----------------*/ + + if(recv_data.size() != recv_data.rpos()) + { + sLog.outError("MovementHandler: player %s (guid %d, account %u) sent a packet (opcode %u) that is %u bytes larger than it should be. Kicked as cheater.", _player->GetName(), _player->GetGUIDLow(), _player->GetSession()->GetAccountId(), recv_data.GetOpcode(), recv_data.size() - recv_data.rpos()); + KickPlayer(); + return; + } + + if (!MaNGOS::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o)) + return; + + /* handle special cases */ + if (MovementFlags & MOVEMENTFLAG_ONTRANSPORT) + { + // transports size limited + // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) + if( movementInfo.t_x > 50 || movementInfo.t_y > 50 || movementInfo.t_z > 50 ) + return; + + if( !MaNGOS::IsValidMapCoord(movementInfo.x+movementInfo.t_x, movementInfo.y+movementInfo.t_y, + movementInfo.z+movementInfo.t_z, movementInfo.o+movementInfo.t_o) ) + return; + + // if we boarded a transport, add us to it + if (!GetPlayer()->m_transport) + { + // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just unmount if the guid can be found in the transport list + for (MapManager::TransportSet::iterator iter = MapManager::Instance().m_Transports.begin(); iter != MapManager::Instance().m_Transports.end(); ++iter) + { + if ((*iter)->GetGUID() == movementInfo.t_guid) + { + // unmount before boarding + _player->RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + + GetPlayer()->m_transport = (*iter); + (*iter)->AddPassenger(GetPlayer()); + break; + } + } + } + } + else if (GetPlayer()->m_transport) // if we were on a transport, leave + { + GetPlayer()->m_transport->RemovePassenger(GetPlayer()); + GetPlayer()->m_transport = NULL; + movementInfo.t_x = 0.0f; + movementInfo.t_y = 0.0f; + movementInfo.t_z = 0.0f; + movementInfo.t_o = 0.0f; + movementInfo.t_time = 0; + } + + // fall damage generation (ignore in flight case that can be triggred also at lags in moment teleportation to another map). + if (recv_data.GetOpcode() == MSG_MOVE_FALL_LAND && !GetPlayer()->isInFlight()) + { + Player *target = GetPlayer(); + + //Players with Feather Fall or low fall time, or physical immunity (charges used) are ignored + if (movementInfo.fallTime > 1100 && !target->isDead() && !target->isGameMaster() && + !target->HasAuraType(SPELL_AURA_HOVER) && !target->HasAuraType(SPELL_AURA_FEATHER_FALL) && + !target->HasAuraType(SPELL_AURA_FLY) && !target->IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL,true) ) + { + //Safe fall, fall time reduction + int32 safe_fall = target->GetTotalAuraModifier(SPELL_AURA_SAFE_FALL); + uint32 fall_time = (movementInfo.fallTime > (safe_fall*10)) ? movementInfo.fallTime - (safe_fall*10) : 0; + + if(fall_time > 1100) //Prevent damage if fall time < 1100 + { + //Fall Damage calculation + float fallperc = float(fall_time)/1100; + uint32 damage = (uint32)(((fallperc*fallperc -1) / 9 * target->GetMaxHealth())*sWorld.getRate(RATE_DAMAGE_FALL)); + + float height = movementInfo.z; + target->UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height); + + if (damage > 0) + { + //Prevent fall damage from being more than the player maximum health + if (damage > target->GetMaxHealth()) + damage = target->GetMaxHealth(); + + // Gust of Wind + if (target->GetDummyAura(43621)) + damage = target->GetMaxHealth()/2; + + target->EnvironmentalDamage(target->GetGUID(), DAMAGE_FALL, damage); + } + + //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction + DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.z, height, target->GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall); + } + } + + //handle fall and logout at the same time (logout started before fall finished) + /* outdated and create problems with sit at stun sometime + if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE)) + { + target->SetStandState(PLAYER_STATE_SIT); + // Can't move + WorldPacket data( SMSG_FORCE_MOVE_ROOT, 12 ); + data.append(target->GetPackGUID()); + data << (uint32)2; + SendPacket( &data ); + } + */ + } + + if(((MovementFlags & MOVEMENTFLAG_SWIMMING) != 0) != GetPlayer()->IsInWater()) + { + // now client not include swimming flag in case jumping under water + GetPlayer()->SetInWater( !GetPlayer()->IsInWater() || GetPlayer()->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) ); + } + + /*----------------------*/ + + /* process position-change */ + recv_data.put(5, getMSTime()); // offset flags(4) + unk(1) + WorldPacket data(recv_data.GetOpcode(), (GetPlayer()->GetPackGUID().size()+recv_data.size())); + data.append(GetPlayer()->GetPackGUID()); + data.append(recv_data.contents(), recv_data.size()); + GetPlayer()->SendMessageToSet(&data, false); + + GetPlayer()->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o); + GetPlayer()->m_movementInfo = movementInfo; + + if(GetPlayer()->isMovingOrTurning()) + GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + + if(movementInfo.z < -500.0f) + { + // NOTE: this is actually called many times while falling + // even after the player has been teleported away + // TODO: discard movement packets after the player is rooted + if(GetPlayer()->isAlive()) + { + GetPlayer()->EnvironmentalDamage(GetPlayer()->GetGUID(),DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); + // change the death state to CORPSE to prevent the death timer from + // starting in the next player update + GetPlayer()->KillPlayer(); + GetPlayer()->BuildPlayerRepop(); + } + + // cancel the death timer here if started + GetPlayer()->RepopAtGraveyard(); + } +} + +void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 8+4+4+1+4+4+4+4+4); + + /* extract packet */ + uint64 guid; + uint8 unkB; + uint32 unk1, flags, time, fallTime; + float x, y, z, orientation; + + uint64 t_GUID; + float t_x, t_y, t_z, t_o; + uint32 t_time; + float s_pitch; + float j_unk1, j_sinAngle, j_cosAngle, j_xyspeed; + float u_unk1; + float newspeed; + + recv_data >> guid; + + // now can skip not our packet + if(_player->GetGUID() != guid) + return; + + // continue parse packet + + recv_data >> unk1; + recv_data >> flags >> unkB >> time; + recv_data >> x >> y >> z >> orientation; + if (flags & MOVEMENTFLAG_ONTRANSPORT) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+8+4+4+4+4+4); + + recv_data >> t_GUID; + recv_data >> t_x >> t_y >> t_z >> t_o >> t_time; + } + if (flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING2)) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + + recv_data >> s_pitch; // pitch, -1.55=looking down, 0=looking straight forward, +1.55=looking up + } + + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + + recv_data >> fallTime; // duration of last jump (when in jump duration from jump begin to now) + + if ((flags & MOVEMENTFLAG_JUMPING) || (flags & MOVEMENTFLAG_FALLING)) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4+4+4+4); + + recv_data >> j_unk1; // ?constant, but different when jumping in water and on land? + recv_data >> j_sinAngle >> j_cosAngle; // sin + cos of angle between orientation0 and players orientation + recv_data >> j_xyspeed; // speed of xy movement + } + + if(flags & MOVEMENTFLAG_SPLINE) + { + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + + recv_data >> u_unk1; // unknown + } + + // recheck + CHECK_PACKET_SIZE(recv_data, recv_data.rpos()+4); + + recv_data >> newspeed; + /*----------------*/ + + // client ACK send one packet for mounted/run case and need skip all except last from its + // in other cases anti-cheat check can be fail in false case + UnitMoveType move_type; + UnitMoveType force_move_type; + + static char const* move_type_name[MAX_MOVE_TYPE] = { "Walk", "Run", "Walkback", "Swim", "Swimback", "Turn", "Fly", "Flyback" }; + + uint16 opcode = recv_data.GetOpcode(); + switch(opcode) + { + case CMSG_FORCE_WALK_SPEED_CHANGE_ACK: move_type = MOVE_WALK; force_move_type = MOVE_WALK; break; + case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: move_type = MOVE_RUN; force_move_type = MOVE_RUN; break; + case CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK: move_type = MOVE_WALKBACK; force_move_type = MOVE_WALKBACK; break; + case CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: move_type = MOVE_SWIM; force_move_type = MOVE_SWIM; break; + case CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: move_type = MOVE_SWIMBACK; force_move_type = MOVE_SWIMBACK; break; + case CMSG_FORCE_TURN_RATE_CHANGE_ACK: move_type = MOVE_TURN; force_move_type = MOVE_TURN; break; + case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: move_type = MOVE_FLY; force_move_type = MOVE_FLY; break; + case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: move_type = MOVE_FLYBACK; force_move_type = MOVE_FLYBACK; break; + default: + sLog.outError("WorldSession::HandleForceSpeedChangeAck: Unknown move type opcode: %u", opcode); + return; + } + + // skip all forced speed changes except last and unexpected + // in run/mounted case used one ACK and it must be skipped.m_forced_speed_changes[MOVE_RUN} store both. + if(_player->m_forced_speed_changes[force_move_type] > 0) + { + --_player->m_forced_speed_changes[force_move_type]; + if(_player->m_forced_speed_changes[force_move_type] > 0) + return; + } + + if (!_player->GetTransport() && fabs(_player->GetSpeed(move_type) - newspeed) > 0.01f) + { + if(_player->GetSpeed(move_type) > newspeed) // must be greater - just correct + { + sLog.outError("%sSpeedChange player %s is NOT correct (must be %f instead %f), force set to correct value", + move_type_name[move_type], _player->GetName(), _player->GetSpeed(move_type), newspeed); + _player->SetSpeed(move_type,_player->GetSpeedRate(move_type),true); + } + else // must be lesser - cheating + { + sLog.outBasic("Player %s from account id %u kicked for incorrect speed (must be %f instead %f)", + _player->GetName(),_player->GetSession()->GetAccountId(),_player->GetSpeed(move_type), newspeed); + _player->GetSession()->KickPlayer(); + } + } +} + +void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data) +{ + sLog.outDebug("WORLD: Recvd CMSG_SET_ACTIVE_MOVER"); + + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid; + recv_data >> guid; + + WorldPacket data(SMSG_TIME_SYNC_REQ, 4); // new 2.0.x, enable movement + data << uint32(0x00000000); // on blizz it increments periodically + SendPacket(&data); +} + +void WorldSession::HandleMountSpecialAnimOpcode(WorldPacket& /*recvdata*/) +{ + //sLog.outDebug("WORLD: Recvd CMSG_MOUNTSPECIAL_ANIM"); + + WorldPacket data(SMSG_MOUNTSPECIAL_ANIM, 8); + data << uint64(GetPlayer()->GetGUID()); + + GetPlayer()->SendMessageToSet(&data, false); +} + +void WorldSession::HandleMoveKnockBackAck( WorldPacket & /*recv_data*/ ) +{ + // CHECK_PACKET_SIZE(recv_data,?); + sLog.outDebug("CMSG_MOVE_KNOCK_BACK_ACK"); + // Currently not used but maybe use later for recheck final player position + // (must be at call same as into "recv_data >> x >> y >> z >> orientation;" + + /* + uint32 flags, time; + float x, y, z, orientation; + uint64 guid; + uint32 sequence; + uint32 ukn1; + float xdirection,ydirection,hspeed,vspeed; + + recv_data >> guid; + recv_data >> sequence; + recv_data >> flags >> time; + recv_data >> x >> y >> z >> orientation; + recv_data >> ukn1; //unknown + recv_data >> vspeed >> xdirection >> ydirection >> hspeed; + + // skip not personal message; + if(GetPlayer()->GetGUID()!=guid) + return; + + // check code + */ +} + +void WorldSession::HandleMoveHoverAck( WorldPacket& /*recv_data*/ ) +{ + sLog.outDebug("CMSG_MOVE_HOVER_ACK"); +} + +void WorldSession::HandleMoveWaterWalkAck(WorldPacket& /*recv_data*/) +{ + sLog.outDebug("CMSG_MOVE_WATER_WALK_ACK"); +} + +void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data,8+1); + + if(!_player->isAlive() || _player->isInCombat() ) + return; + + uint64 summoner_guid; + bool agree; + recv_data >> summoner_guid; + recv_data >> agree; + + _player->SummonIfPossible(agree); +} diff --git a/src/game/NPCHandler.cpp b/src/game/NPCHandler.cpp new file mode 100644 index 00000000000..5739f56e328 --- /dev/null +++ b/src/game/NPCHandler.cpp @@ -0,0 +1,832 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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); + + // Lazy loading at first access + unit->LoadTrainerSpells(); + + // 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; + } + + Creature::SpellsList const& trainer_spells = unit->GetTrainerSpells(); + + WorldPacket data( SMSG_TRAINER_LIST, 8+4+4+trainer_spells.size()*38 + strTitle.size()+1); + data << guid; + data << uint32(unit->GetTrainerType()); + + size_t count_pos = data.wpos(); + data << uint32(trainer_spells.size()); + + // reputation discount + float fDiscountMod = _player->GetReputationPriceDiscount(unit); + + uint32 count = 0; + for(Creature::SpellsList::const_iterator itr = trainer_spells.begin(); itr != trainer_spells.end(); ++itr) + { + if(!_player->IsSpellFitByClassAndRace(itr->spell->Id)) + continue; + + ++count; + + bool primary_prof_first_rank = spellmgr.IsPrimaryProfessionFirstRankSpell(itr->spell->Id); + + SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(itr->spell->Id); + + data << uint32(itr->spell->Id); + data << uint8(_player->GetTrainerSpellState(&*itr)); + data << uint32(floor(itr->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(itr->reqlevel ? itr->reqlevel : itr->spell->spellLevel); + data << uint32(itr->reqskill); + data << uint32(itr->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(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); + + // Lazy loading at first access + unit->LoadTrainerSpells(); + + if(!unit->isCanTrainingOf(_player,true)) + return; + + TrainerSpell const* trainer_spell = NULL; + + // check present spell in trainer spell list + Creature::SpellsList const& trainer_spells = unit->GetTrainerSpells(); + for(Creature::SpellsList::const_iterator itr = trainer_spells.begin(); itr != trainer_spells.end(); ++itr) + { + if(itr->spell->Id == spellId) + { + trainer_spell = &*itr; + break; + } + } + + // not found, cheat? + 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->Id); + + 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(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/NPCHandler.h b/src/game/NPCHandler.h new file mode 100644 index 00000000000..1f42be22969 --- /dev/null +++ b/src/game/NPCHandler.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __NPCHANDLER_H +#define __NPCHANDLER_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 + +struct PageText +{ + uint32 Page_ID; + char * Text; + + uint32 Next_Page; +}; + +// 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 + +struct QEmote +{ + uint32 _Emote; + uint32 _Delay; +}; + +struct GossipTextOption +{ + std::string Text_0; + std::string Text_1; + uint32 Language; + float Probability; + QEmote Emotes[3]; +}; + +struct GossipText +{ + uint32 Text_ID; + GossipTextOption Options[8]; +}; + +struct PageTextLocale +{ + std::vector Text; +}; + +struct NpcTextLocale +{ + NpcTextLocale() { Text_0.resize(8); Text_1.resize(8); } + + std::vector > Text_0; + std::vector > Text_1; +}; +#endif diff --git a/src/game/NullCreatureAI.cpp b/src/game/NullCreatureAI.cpp new file mode 100644 index 00000000000..81f72c17da9 --- /dev/null +++ b/src/game/NullCreatureAI.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "NullCreatureAI.h" + +NullCreatureAI::~NullCreatureAI() +{ +} diff --git a/src/game/NullCreatureAI.h b/src/game/NullCreatureAI.h new file mode 100644 index 00000000000..7404e0a40f8 --- /dev/null +++ b/src/game/NullCreatureAI.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_NULLCREATUREAI_H +#define MANGOS_NULLCREATUREAI_H + +#include "CreatureAI.h" + +class MANGOS_DLL_DECL NullCreatureAI : public CreatureAI +{ + public: + + NullCreatureAI(Creature &) {} + NullCreatureAI() {} + + ~NullCreatureAI(); + + void MoveInLineOfSight(Unit *) {} + void AttackStart(Unit *) {} + void EnterEvadeMode() {} + + bool IsVisible(Unit *) const { return false; } + + void UpdateAI(const uint32) {} + static int Permissible(const Creature *) { return PERMIT_BASE_IDLE; } +}; +#endif diff --git a/src/game/Object.cpp b/src/game/Object.cpp new file mode 100644 index 00000000000..b3e61e102d3 --- /dev/null +++ b/src/game/Object.cpp @@ -0,0 +1,1631 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 "ObjectPosSelector.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); +} + +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 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 say_worker(say_do); + TypeContainerVisitor, WorldTypeMapContainer > message(say_worker); + CellLock 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 say_worker(say_do); + TypeContainerVisitor, WorldTypeMapContainer > message(say_worker); + CellLock 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 say_worker(say_do); + TypeContainerVisitor, WorldTypeMapContainer > message(say_worker); + CellLock 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; +} + +namespace MaNGOS +{ + class NearUsedPosDo + { + public: + NearUsedPosDo(WorldObject const& obj, WorldObject const* searcher, float angle, ObjectPosSelector& selector) + : i_object(obj), i_searcher(searcher), i_angle(angle), i_selector(selector) {} + + void operator()(Corpse*) const {} + void operator()(DynamicObject*) const {} + + void operator()(Creature* c) const + { + // skip self or target + if(c==i_searcher || c==&i_object) + return; + + float x,y,z; + + if( !c->isAlive() || c->hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED) || + !c->GetMotionMaster()->GetDestination(x,y,z) ) + { + x = c->GetPositionX(); + y = c->GetPositionY(); + } + + add(c,x,y); + } + + template + void operator()(T* u) const + { + // skip self or target + if(u==i_searcher || u==&i_object) + return; + + float x,y; + + x = u->GetPositionX(); + y = u->GetPositionY(); + + add(u,x,y); + } + + // we must add used pos that can fill places around center + void add(WorldObject* u, float x, float y) const + { + // dist include size of u + float dist2d = i_object.GetDistance2d(x,y); + + // u is too nearest to i_object + if(dist2d + i_object.GetObjectSize() + u->GetObjectSize() < i_selector.m_dist - i_selector.m_size) + return; + + // u is too far away from i_object + if(dist2d + i_object.GetObjectSize() - u->GetObjectSize() > i_selector.m_dist + i_selector.m_size) + return; + + float angle = i_object.GetAngle(u)-i_angle; + + // move angle to range -pi ... +pi + while( angle > M_PI) + angle -= 2.0f * M_PI; + while(angle < -M_PI) + angle += 2.0f * M_PI; + + i_selector.AddUsedPos(u->GetObjectSize(),angle,dist2d + i_object.GetObjectSize()); + } + private: + WorldObject const& i_object; + WorldObject const* i_searcher; + float i_angle; + ObjectPosSelector& i_selector; + }; +} // namespace MaNGOS + +//=================================================================================================== + +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(); + + // if detection disabled, return first point + if(!sWorld.getConfig(CONFIG_DETECT_POS_COLLISION)) + { + UpdateGroundPositionZ(x,y,z); // update to LOS height if available + return; + } + + // or remember first point + float first_x = x; + float first_y = y; + bool first_los_conflict = false; // first point LOS problems + + // prepare selector for work + ObjectPosSelector selector(GetPositionX(),GetPositionY(),GetObjectSize(),distance2d+searcher_size); + + // adding used positions around object + { + CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + MaNGOS::NearUsedPosDo u_do(*this,searcher,absAngle,selector); + MaNGOS::WorldObjectWorker worker(u_do); + + TypeContainerVisitor, GridTypeMapContainer > grid_obj_worker(worker); + TypeContainerVisitor, WorldTypeMapContainer > world_obj_worker(worker); + + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_obj_worker, *MapManager::Instance().GetMap(GetMapId(), this)); + cell_lock->Visit(cell_lock, world_obj_worker, *MapManager::Instance().GetMap(GetMapId(), this)); + } + + // maybe can just place in primary position + if( selector.CheckOriginal() ) + { + UpdateGroundPositionZ(x,y,z); // update to LOS height if available + + if(IsWithinLOS(x,y,z)) + return; + + first_los_conflict = true; // first point have LOS problems + } + + float angle; // candidate of angle for free pos + + // special case when one from list empty and then empty side preferred + if(selector.FirstAngle(angle)) + { + GetNearPoint2D(x,y,distance2d,absAngle+angle); + z = GetPositionZ(); + UpdateGroundPositionZ(x,y,z); // update to LOS height if available + + if(IsWithinLOS(x,y,z)) + return; + } + + // set first used pos in lists + selector.InitializeAngle(); + + // select in positions after current nodes (selection one by one) + while(selector.NextAngle(angle)) // angle for free pos + { + GetNearPoint2D(x,y,distance2d,absAngle+angle); + z = GetPositionZ(); + UpdateGroundPositionZ(x,y,z); // update to LOS height if available + + if(IsWithinLOS(x,y,z)) + return; + } + + // BAD NEWS: not free pos (or used or have LOS problems) + // Attempt find _used_ pos without LOS problem + + if(!first_los_conflict) + { + x = first_x; + y = first_y; + + UpdateGroundPositionZ(x,y,z); // update to LOS height if available + return; + } + + // special case when one from list empty and then empty side preferred + if( selector.IsNonBalanced() ) + { + if(!selector.FirstAngle(angle)) // _used_ pos + { + GetNearPoint2D(x,y,distance2d,absAngle+angle); + z = GetPositionZ(); + UpdateGroundPositionZ(x,y,z); // update to LOS height if available + + if(IsWithinLOS(x,y,z)) + return; + } + } + + // set first used pos in lists + selector.InitializeAngle(); + + // select in positions after current nodes (selection one by one) + while(selector.NextUsedAngle(angle)) // angle for used pos but maybe without LOS problem + { + GetNearPoint2D(x,y,distance2d,absAngle+angle); + z = GetPositionZ(); + UpdateGroundPositionZ(x,y,z); // update to LOS height if available + + if(IsWithinLOS(x,y,z)) + return; + } + + // BAD BAD NEWS: all found pos (free and used) have LOS problem :( + x = first_x; + y = first_y; + + UpdateGroundPositionZ(x,y,z); // update to LOS height if available +} diff --git a/src/game/Object.h b/src/game/Object.h new file mode 100644 index 00000000000..c870e9452c9 --- /dev/null +++ b/src/game/Object.h @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _OBJECT_H +#define _OBJECT_H + +#include "Common.h" +#include "ByteBuffer.h" +#include "UpdateFields.h" +#include "UpdateData.h" +#include "GameSystem/GridReference.h" +#include "ObjectDefines.h" + +#include +#include + +#define CONTACT_DISTANCE 0.5f +#define INTERACTION_DISTANCE 5 +#define ATTACK_DISTANCE 5 +#define DETECT_DISTANCE 20 // max distance to successful detect stealthed unit +#define MAX_VISIBILITY_DISTANCE (5*SIZE_OF_GRID_CELL/2.0f) // max distance for visible object show, limited by active zone for player based at cell size (active zone = 5x5 cells) +#define DEFAULT_VISIBILITY_DISTANCE (SIZE_OF_GRID_CELL) // default visible distance + +#define DEFAULT_WORLD_OBJECT_SIZE 0.388999998569489f // player size, also currently used (correctly?) for any non Unit world objects + +enum TypeMask +{ + TYPEMASK_OBJECT = 0x0001, + TYPEMASK_ITEM = 0x0002, + TYPEMASK_CONTAINER = 0x0006, // TYPEMASK_ITEM | 0x0004 + TYPEMASK_UNIT = 0x0008, + TYPEMASK_PLAYER = 0x0010, + TYPEMASK_GAMEOBJECT = 0x0020, + TYPEMASK_DYNAMICOBJECT = 0x0040, + TYPEMASK_CORPSE = 0x0080, + TYPEMASK_AIGROUP = 0x0100, + TYPEMASK_AREATRIGGER = 0x0200 +}; + +enum TypeID +{ + TYPEID_OBJECT = 0, + TYPEID_ITEM = 1, + TYPEID_CONTAINER = 2, + TYPEID_UNIT = 3, + TYPEID_PLAYER = 4, + TYPEID_GAMEOBJECT = 5, + TYPEID_DYNAMICOBJECT = 6, + TYPEID_CORPSE = 7, + TYPEID_AIGROUP = 8, + TYPEID_AREATRIGGER = 9 +}; + +uint32 GuidHigh2TypeId(uint32 guid_hi); + +enum TempSummonType +{ + TEMPSUMMON_TIMED_OR_DEAD_DESPAWN = 1, // despawns after a specified time OR when the creature disappears + TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN = 2, // despawns after a specified time OR when the creature dies + TEMPSUMMON_TIMED_DESPAWN = 3, // despawns after a specified time + TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT = 4, // despawns after a specified time after the creature is out of combat + TEMPSUMMON_CORPSE_DESPAWN = 5, // despawns instantly after death + TEMPSUMMON_CORPSE_TIMED_DESPAWN = 6, // despawns after a specified time after death + TEMPSUMMON_DEAD_DESPAWN = 7, // despawns when the creature disappears + TEMPSUMMON_MANUAL_DESPAWN = 8 // despawns when UnSummon() is called +}; + +class WorldPacket; +class UpdateData; +class ByteBuffer; +class WorldSession; +class Creature; +class Player; +class Map; +class UpdateMask; +class InstanceData; + +typedef HM_NAMESPACE::hash_map UpdateDataMapType; + +struct WorldLocation +{ + uint32 mapid; + float x; + float y; + float z; + float o; + explicit WorldLocation(uint32 mapid = 0, float x = 0, float y = 0, float z = 0, float o = 0) + : mapid(mapid), x(x), y(y), z(z), o(o) {} + WorldLocation(WorldLocation const &loc) + : mapid(loc.mapid), x(loc.x), y(loc.y), z(loc.z), o(loc.o) {} +}; + +class MANGOS_DLL_SPEC Object +{ + public: + virtual ~Object ( ); + + const bool& IsInWorld() const { return m_inWorld; } + virtual void AddToWorld() + { + if(m_inWorld) + return; + + m_inWorld = true; + + // synchronize values mirror with values array (changes will send in updatecreate opcode any way + ClearUpdateMask(true); + } + virtual void RemoveFromWorld() + { + // if we remove from world then sending changes not required + if(m_uint32Values) + ClearUpdateMask(true); + m_inWorld = false; + } + + const uint64& GetGUID() const { return GetUInt64Value(0); } + uint32 GetGUIDLow() const { return GUID_LOPART(GetUInt64Value(0)); } + uint32 GetGUIDMid() const { return GUID_ENPART(GetUInt64Value(0)); } + uint32 GetGUIDHigh() const { return GUID_HIPART(GetUInt64Value(0)); } + const ByteBuffer& GetPackGUID() const { return m_PackGUID; } + uint32 GetEntry() const { return GetUInt32Value(OBJECT_FIELD_ENTRY); } + + uint8 GetTypeId() const { return m_objectTypeId; } + bool isType(uint16 mask) const { return (mask & m_objectType); } + + virtual void BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const; + void SendUpdateToPlayer(Player* player); + + void BuildValuesUpdateBlockForPlayer( UpdateData *data, Player *target ) const; + void BuildOutOfRangeUpdateBlock( UpdateData *data ) const; + void BuildMovementUpdateBlock( UpdateData * data, uint32 flags = 0 ) const; + void BuildUpdate(UpdateDataMapType &); + + virtual void DestroyForPlayer( Player *target ) const; + + const int32& GetInt32Value( uint16 index ) const + { + ASSERT( index < m_valuesCount || PrintIndexError( index , false) ); + return m_int32Values[ index ]; + } + + const uint32& GetUInt32Value( uint16 index ) const + { + ASSERT( index < m_valuesCount || PrintIndexError( index , false) ); + return m_uint32Values[ index ]; + } + + const uint64& GetUInt64Value( uint16 index ) const + { + ASSERT( index + 1 < m_valuesCount || PrintIndexError( index , false) ); + return *((uint64*)&(m_uint32Values[ index ])); + } + + const float& GetFloatValue( uint16 index ) const + { + ASSERT( index < m_valuesCount || PrintIndexError( index , false ) ); + return m_floatValues[ index ]; + } + + uint8 GetByteValue( uint16 index, uint8 offset) const + { + ASSERT( index < m_valuesCount || PrintIndexError( index , false) ); + ASSERT( offset < 4 ); + return *(((uint8*)&m_uint32Values[ index ])+offset); + } + + uint8 GetUInt16Value( uint16 index, uint8 offset) const + { + ASSERT( index < m_valuesCount || PrintIndexError( index , false) ); + ASSERT( offset < 2 ); + return *(((uint16*)&m_uint32Values[ index ])+offset); + } + + void SetInt32Value( uint16 index, int32 value ); + void SetUInt32Value( uint16 index, uint32 value ); + void SetUInt64Value( uint16 index, const uint64 &value ); + void SetFloatValue( uint16 index, float value ); + void SetByteValue( uint16 index, uint8 offset, uint8 value ); + void SetUInt16Value( uint16 index, uint8 offset, uint16 value ); + void SetInt16Value( uint16 index, uint8 offset, int16 value ) { SetUInt16Value(index,offset,(uint16)value); } + void SetStatFloatValue( uint16 index, float value); + void SetStatInt32Value( uint16 index, int32 value); + + void ApplyModUInt32Value(uint16 index, int32 val, bool apply); + void ApplyModInt32Value(uint16 index, int32 val, bool apply); + void ApplyModUInt64Value(uint16 index, int32 val, bool apply); + void ApplyModPositiveFloatValue( uint16 index, float val, bool apply); + void ApplyModSignedFloatValue( uint16 index, float val, bool apply); + + void ApplyPercentModFloatValue(uint16 index, float val, bool apply) + { + val = val != -100.0f ? val : -99.9f ; + SetFloatValue(index, GetFloatValue(index) * (apply?(100.0f+val)/100.0f : 100.0f / (100.0f+val)) ); + } + + void SetFlag( uint16 index, uint32 newFlag ); + void RemoveFlag( uint16 index, uint32 oldFlag ); + + void ToggleFlag( uint16 index, uint32 flag) + { + if(HasFlag(index, flag)) + RemoveFlag(index, flag); + else + SetFlag(index, flag); + } + + bool HasFlag( uint16 index, uint32 flag ) const + { + ASSERT( index < m_valuesCount || PrintIndexError( index , false ) ); + return (m_uint32Values[ index ] & flag) != 0; + } + + void ApplyModFlag( uint16 index, uint32 flag, bool apply) + { + if(apply) SetFlag(index,flag); else RemoveFlag(index,flag); + } + + void SetFlag64( uint16 index, uint64 newFlag ) + { + uint64 oldval = GetUInt64Value(index); + uint64 newval = oldval | newFlag; + SetUInt64Value(index,newval); + } + + void RemoveFlag64( uint16 index, uint64 oldFlag ) + { + uint64 oldval = GetUInt64Value(index); + uint64 newval = oldval & ~oldFlag; + SetUInt64Value(index,newval); + } + + void ToggleFlag64( uint16 index, uint64 flag) + { + if(HasFlag64(index, flag)) + RemoveFlag64(index, flag); + else + SetFlag64(index, flag); + } + + bool HasFlag64( uint16 index, uint64 flag ) const + { + ASSERT( index < m_valuesCount || PrintIndexError( index , false ) ); + return (GetUInt64Value( index ) & flag) != 0; + } + + void ApplyModFlag64( uint16 index, uint64 flag, bool apply) + { + if(apply) SetFlag64(index,flag); else RemoveFlag64(index,flag); + } + + void ClearUpdateMask(bool remove); + void SendUpdateObjectToAllExcept(Player* exceptPlayer); + + bool LoadValues(const char* data); + + uint16 GetValuesCount() const { return m_valuesCount; } + + void InitValues() { _InitValues(); } + + virtual bool hasQuest(uint32 /* quest_id */) const { return false; } + virtual bool hasInvolvedQuest(uint32 /* quest_id */) const { return false; } + protected: + + Object ( ); + + void _InitValues(); + void _Create (uint32 guidlow, uint32 entry, HighGuid guidhigh); + + virtual void _SetUpdateBits(UpdateMask *updateMask, Player *target) const; + + virtual void _SetCreateBits(UpdateMask *updateMask, Player *target) const; + void _BuildMovementUpdate(ByteBuffer * data, uint8 flags, uint32 flags2 ) const; + void _BuildValuesUpdate(uint8 updatetype, ByteBuffer *data, UpdateMask *updateMask, Player *target ) const; + + uint16 m_objectType; + + uint8 m_objectTypeId; + uint8 m_updateFlag; + + union + { + int32 *m_int32Values; + uint32 *m_uint32Values; + float *m_floatValues; + }; + + uint32 *m_uint32Values_mirror; + + uint16 m_valuesCount; + + bool m_objectUpdated; + + private: + bool m_inWorld; + + ByteBuffer m_PackGUID; + + // for output helpfull error messages from asserts + bool PrintIndexError(uint32 index, bool set) const; + Object(const Object&); // prevent generation copy constructor + Object& operator=(Object const&); // prevent generation assigment operator +}; + +class MANGOS_DLL_SPEC WorldObject : public Object +{ + public: + virtual ~WorldObject ( ) {} + + virtual void Update ( uint32 /*time_diff*/ ) { } + + void _Create( uint32 guidlow, HighGuid guidhigh, uint32 mapid ); + + void Relocate(float x, float y, float z, float orientation) + { + m_positionX = x; + m_positionY = y; + m_positionZ = z; + m_orientation = orientation; + } + + void Relocate(float x, float y, float z) + { + m_positionX = x; + m_positionY = y; + m_positionZ = z; + } + + void Relocate(WorldLocation const & loc) + { + SetMapId(loc.mapid); + Relocate(loc.x, loc.y, loc.z, loc.o); + } + + void SetOrientation(float orientation) { m_orientation = orientation; } + + float GetPositionX( ) const { return m_positionX; } + float GetPositionY( ) const { return m_positionY; } + float GetPositionZ( ) const { return m_positionZ; } + void GetPosition( float &x, float &y, float &z ) const + { x = m_positionX; y = m_positionY; z = m_positionZ; } + void GetPosition( WorldLocation &loc ) const + { loc.mapid = GetMapId(); GetPosition(loc.x, loc.y, loc.z); loc.o = GetOrientation(); } + float GetOrientation( ) const { return m_orientation; } + void GetNearPoint2D( float &x, float &y, float distance, float absAngle) const; + void GetNearPoint( WorldObject const* searcher, float &x, float &y, float &z, float searcher_size, float distance2d,float absAngle) const; + void GetClosePoint(float &x, float &y, float &z, float size, float distance2d = 0, float angle = 0) const + { + // angle calculated from current orientation + GetNearPoint(NULL,x,y,z,size,distance2d,GetOrientation() + angle); + } + void GetContactPoint( const WorldObject* obj, float &x, float &y, float &z, float distance2d = CONTACT_DISTANCE) const + { + // angle to face `obj` to `this` using distance includes size of `obj` + GetNearPoint(obj,x,y,z,obj->GetObjectSize(),distance2d,GetAngle( obj )); + } + + float GetObjectSize() const + { + return ( m_valuesCount > UNIT_FIELD_BOUNDINGRADIUS ) ? m_floatValues[UNIT_FIELD_BOUNDINGRADIUS] : DEFAULT_WORLD_OBJECT_SIZE; + } + bool IsPositionValid() const; + void UpdateGroundPositionZ(float x, float y, float &z) const; + + void GetRandomPoint( float x, float y, float z, float distance, float &rand_x, float &rand_y, float &rand_z ) const; + + void SetMapId(uint32 newMap) { m_mapId = newMap; } + + uint32 GetMapId() const { return m_mapId; } + + uint32 GetZoneId() const; + uint32 GetAreaId() const; + + InstanceData* GetInstanceData(); + + const char* GetName() const { return m_name.c_str(); } + void SetName(std::string newname) { m_name=newname; } + + float GetDistance( const WorldObject* obj ) const; + float GetDistance(const float x, const float y, const float z) const; + float GetDistance2d(const WorldObject* obj) const; + float GetDistance2d(const float x, const float y) const; + float GetDistanceZ(const WorldObject* obj) const; + bool IsInMap(const WorldObject* obj) const { return GetMapId()==obj->GetMapId() && GetInstanceId()==obj->GetInstanceId(); } + bool IsWithinDistInMap(const WorldObject* obj, const float dist2compare) const; + bool IsWithinLOS(const float x, const float y, const float z ) const; + bool IsWithinLOSInMap(const WorldObject* obj) const; + + float GetAngle( const WorldObject* obj ) const; + float GetAngle( const float x, const float y ) const; + bool HasInArc( const float arcangle, const WorldObject* obj ) const; + + virtual void SendMessageToSet(WorldPacket *data, bool self); + virtual void SendMessageToSetInRange(WorldPacket *data, float dist, bool self); + void BuildHeartBeatMsg( WorldPacket *data ) const; + void BuildTeleportAckMsg( WorldPacket *data, float x, float y, float z, float ang) const; + bool IsBeingTeleported() { return mSemaphoreTeleport; } + void SetSemaphoreTeleport(bool semphsetting) { mSemaphoreTeleport = semphsetting; } + + void MonsterSay(const char* text, uint32 language, uint64 TargetGuid); + void MonsterYell(const char* text, uint32 language, uint64 TargetGuid); + void MonsterTextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote = false); + void MonsterWhisper(const char* text, uint64 receiver, bool IsBossWhisper = false); + void MonsterSay(int32 textId, uint32 language, uint64 TargetGuid); + void MonsterYell(int32 textId, uint32 language, uint64 TargetGuid); + void MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote = false); + void MonsterWhisper(int32 textId, uint64 receiver, bool IsBossWhisper = false); + void BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const* text, uint32 language, char const* name, uint64 TargetGuid) const; + + void SendObjectDeSpawnAnim(uint64 guid); + + virtual void SaveRespawnTime() {} + + uint32 GetInstanceId() const { return m_InstanceId; } + void SetInstanceId(uint32 val) { m_InstanceId = val; } + + void AddObjectToRemoveList(); + + // main visibility check function in normal case (ignore grey zone distance check) + bool isVisibleFor(Player const* u) const { return isVisibleForInState(u,false); } + + // low level function for visibility change code, must be define in all main world object subclasses + virtual bool isVisibleForInState(Player const* u, bool inVisibleList) const = 0; + + Map * GetMap() const; + Map const* GetBaseMap() const; + Creature* SummonCreature(uint32 id, float x, float y, float z, float ang,TempSummonType spwtype,uint32 despwtime); + + protected: + explicit WorldObject(); + std::string m_name; + + private: + uint32 m_mapId; + + float m_positionX; + float m_positionY; + float m_positionZ; + float m_orientation; + + bool mSemaphoreTeleport; + + uint32 m_InstanceId; +}; +#endif diff --git a/src/game/ObjectAccessor.cpp b/src/game/ObjectAccessor.cpp new file mode 100644 index 00000000000..702bdc72123 --- /dev/null +++ b/src/game/ObjectAccessor.cpp @@ -0,0 +1,648 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "Policies/SingletonImp.h" +#include "Player.h" +#include "Creature.h" +#include "GameObject.h" +#include "DynamicObject.h" +#include "Corpse.h" +#include "WorldSession.h" +#include "WorldPacket.h" +#include "Item.h" +#include "Corpse.h" +#include "GridNotifiers.h" +#include "MapManager.h" +#include "Map.h" +#include "CellImpl.h" +#include "GridNotifiersImpl.h" +#include "Opcodes.h" +#include "ObjectDefines.h" +#include "MapInstanced.h" + +#include + +#define CLASS_LOCK MaNGOS::ClassLevelLockable +INSTANTIATE_SINGLETON_2(ObjectAccessor, CLASS_LOCK); +INSTANTIATE_CLASS_MUTEX(ObjectAccessor, ZThread::FastMutex); + +namespace MaNGOS +{ + + struct MANGOS_DLL_DECL BuildUpdateForPlayer + { + Player &i_player; + UpdateDataMapType &i_updatePlayers; + + BuildUpdateForPlayer(Player &player, UpdateDataMapType &data_map) : i_player(player), i_updatePlayers(data_map) {} + + void Visit(PlayerMapType &m) + { + for(PlayerMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + if( iter->getSource() == &i_player ) + continue; + + UpdateDataMapType::iterator iter2 = i_updatePlayers.find(iter->getSource()); + if( iter2 == i_updatePlayers.end() ) + { + std::pair p = i_updatePlayers.insert( ObjectAccessor::UpdateDataValueType(iter->getSource(), UpdateData()) ); + assert(p.second); + iter2 = p.first; + } + + i_player.BuildValuesUpdateBlockForPlayer(&iter2->second, iter2->first); + } + } + + template void Visit(GridRefManager &) {} + }; +} + +ObjectAccessor::ObjectAccessor() {} +ObjectAccessor::~ObjectAccessor() {} + +Creature* +ObjectAccessor::GetNPCIfCanInteractWith(Player const &player, uint64 guid, uint32 npcflagmask) +{ + // unit checks + if (!guid) + return NULL; + + // exist + Creature *unit = GetCreature(player, guid); + if (!unit) + return NULL; + + // player check + if(!player.CanInteractWithNPCs(!unit->isSpiritService())) + return NULL; + + // appropriate npc type + if(npcflagmask && !unit->HasFlag( UNIT_NPC_FLAGS, npcflagmask )) + return NULL; + + // alive or spirit healer + if(!unit->isAlive() && (!unit->isSpiritService() || player.isAlive() )) + return NULL; + + // not allow interaction under control + if(unit->GetCharmerOrOwnerGUID()) + return NULL; + + // not enemy + if( unit->IsHostileTo(&player)) + return NULL; + + // not unfriendly + FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(unit->getFaction()); + if(factionTemplate) + { + FactionEntry const* faction = sFactionStore.LookupEntry(factionTemplate->faction); + if( faction->reputationListID >= 0 && player.GetReputationRank(faction) <= REP_UNFRIENDLY) + return NULL; + } + + // not too far + if(!unit->IsWithinDistInMap(&player,INTERACTION_DISTANCE)) + return NULL; + + return unit; +} + +Creature* +ObjectAccessor::GetCreatureOrPet(WorldObject const &u, uint64 guid) +{ + if(Creature *unit = GetPet(guid)) + return unit; + + return GetCreature(u, guid); +} + +Creature* +ObjectAccessor::GetCreature(WorldObject const &u, uint64 guid) +{ + Creature * ret = GetObjectInWorld(guid, (Creature*)NULL); + if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL; + return ret; +} + +Unit* +ObjectAccessor::GetUnit(WorldObject const &u, uint64 guid) +{ + if(!guid) + return NULL; + + if(IS_PLAYER_GUID(guid)) + return FindPlayer(guid); + + return GetCreatureOrPet(u, guid); +} + +Corpse* +ObjectAccessor::GetCorpse(WorldObject const &u, uint64 guid) +{ + Corpse * ret = GetObjectInWorld(guid, (Corpse*)NULL); + if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL; + return ret; +} + +Object* ObjectAccessor::GetObjectByTypeMask(Player const &p, uint64 guid, uint32 typemask) +{ + Object *obj = NULL; + + if(typemask & TYPEMASK_PLAYER) + { + obj = FindPlayer(guid); + if(obj) return obj; + } + + if(typemask & TYPEMASK_UNIT) + { + obj = GetCreatureOrPet(p,guid); + if(obj) return obj; + } + + if(typemask & TYPEMASK_GAMEOBJECT) + { + obj = GetGameObject(p,guid); + if(obj) return obj; + } + + if(typemask & TYPEMASK_DYNAMICOBJECT) + { + obj = GetDynamicObject(p,guid); + if(obj) return obj; + } + + if(typemask & TYPEMASK_ITEM) + { + obj = p.GetItemByGuid( guid ); + if(obj) return obj; + } + + return NULL; +} + +GameObject* +ObjectAccessor::GetGameObject(WorldObject const &u, uint64 guid) +{ + GameObject * ret = GetObjectInWorld(guid, (GameObject*)NULL); + if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL; + return ret; +} + +DynamicObject* +ObjectAccessor::GetDynamicObject(Unit const &u, uint64 guid) +{ + DynamicObject * ret = GetObjectInWorld(guid, (DynamicObject*)NULL); + if(ret && ret->GetMapId() != u.GetMapId()) ret = NULL; + return ret; +} + +Player* +ObjectAccessor::FindPlayer(uint64 guid) +{ + return GetObjectInWorld(guid, (Player*)NULL); +} + +Player* +ObjectAccessor::FindPlayerByName(const char *name) +{ + //TODO: Player Guard + HashMapHolder::MapType& m = HashMapHolder::GetContainer(); + HashMapHolder::MapType::iterator iter = m.begin(); + for(; iter != m.end(); ++iter) + if( ::strcmp(name, iter->second->GetName()) == 0 ) + return iter->second; + return NULL; +} + +void +ObjectAccessor::SaveAllPlayers() +{ + Guard guard(*HashMapHolder::GetLock()); + HashMapHolder::MapType& m = HashMapHolder::GetContainer(); + HashMapHolder::MapType::iterator itr = m.begin(); + for(; itr != m.end(); ++itr) + itr->second->SaveToDB(); +} + +void +ObjectAccessor::_update() +{ + UpdateDataMapType update_players; + { + Guard guard(i_updateGuard); + while(!i_objects.empty()) + { + Object* obj = *i_objects.begin(); + i_objects.erase(i_objects.begin()); + if (!obj) + continue; + _buildUpdateObject(obj, update_players); + obj->ClearUpdateMask(false); + } + } + + WorldPacket packet; // here we allocate a std::vector with a size of 0x10000 + for(UpdateDataMapType::iterator iter = update_players.begin(); iter != update_players.end(); ++iter) + { + iter->second.BuildPacket(&packet); + iter->first->GetSession()->SendPacket(&packet); + packet.clear(); // clean the string + } +} + +void +ObjectAccessor::UpdateObject(Object* obj, Player* exceptPlayer) +{ + UpdateDataMapType update_players; + obj->BuildUpdate(update_players); + + WorldPacket packet; + for(UpdateDataMapType::iterator iter = update_players.begin(); iter != update_players.end(); ++iter) + { + if(iter->first == exceptPlayer) + continue; + + iter->second.BuildPacket(&packet); + iter->first->GetSession()->SendPacket(&packet); + packet.clear(); + } +} + +void +ObjectAccessor::AddUpdateObject(Object *obj) +{ + Guard guard(i_updateGuard); + i_objects.insert(obj); +} + +void +ObjectAccessor::RemoveUpdateObject(Object *obj) +{ + Guard guard(i_updateGuard); + std::set::iterator iter = i_objects.find(obj); + if( iter != i_objects.end() ) + i_objects.erase( iter ); +} + +void +ObjectAccessor::_buildUpdateObject(Object *obj, UpdateDataMapType &update_players) +{ + bool build_for_all = true; + Player *pl = NULL; + if( obj->isType(TYPEMASK_ITEM) ) + { + Item *item = static_cast(obj); + pl = item->GetOwner(); + build_for_all = false; + } + + if( pl != NULL ) + _buildPacket(pl, obj, update_players); + + // Capt: okey for all those fools who think its a real fix + // THIS IS A TEMP FIX + if( build_for_all ) + { + WorldObject * temp = dynamic_cast(obj); + + //assert(dynamic_cast(obj)!=NULL); + if (temp) + _buildChangeObjectForPlayer(temp, update_players); + else + sLog.outDebug("ObjectAccessor: Ln 405 Temp bug fix"); + } +} + +void +ObjectAccessor::_buildPacket(Player *pl, Object *obj, UpdateDataMapType &update_players) +{ + UpdateDataMapType::iterator iter = update_players.find(pl); + + if( iter == update_players.end() ) + { + std::pair p = update_players.insert( UpdateDataValueType(pl, UpdateData()) ); + assert(p.second); + iter = p.first; + } + + obj->BuildValuesUpdateBlockForPlayer(&iter->second, iter->first); +} + +void +ObjectAccessor::_buildChangeObjectForPlayer(WorldObject *obj, UpdateDataMapType &update_players) +{ + CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + WorldObjectChangeAccumulator notifier(*obj, update_players); + TypeContainerVisitor player_notifier(notifier); + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, player_notifier, *MapManager::Instance().GetMap(obj->GetMapId(), obj)); +} + +Pet* +ObjectAccessor::GetPet(uint64 guid) +{ + return GetObjectInWorld(guid, (Pet*)NULL); +} + +Corpse* +ObjectAccessor::GetCorpseForPlayerGUID(uint64 guid) +{ + Guard guard(i_corpseGuard); + + Player2CorpsesMapType::iterator iter = i_player2corpse.find(guid); + if( iter == i_player2corpse.end() ) return NULL; + + assert(iter->second->GetType() != CORPSE_BONES); + + return iter->second; +} + +void +ObjectAccessor::RemoveCorpse(Corpse *corpse) +{ + assert(corpse && corpse->GetType() != CORPSE_BONES); + + Guard guard(i_corpseGuard); + Player2CorpsesMapType::iterator iter = i_player2corpse.find(corpse->GetOwnerGUID()); + if( iter == i_player2corpse.end() ) + return; + + // build mapid*cellid -> guid_set map + CellPair cell_pair = MaNGOS::ComputeCellPair(corpse->GetPositionX(), corpse->GetPositionY()); + uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord; + + objmgr.DeleteCorpseCellData(corpse->GetMapId(),cell_id,corpse->GetOwnerGUID()); + corpse->RemoveFromWorld(); + + i_player2corpse.erase(iter); +} + +void +ObjectAccessor::AddCorpse(Corpse *corpse) +{ + assert(corpse && corpse->GetType() != CORPSE_BONES); + + Guard guard(i_corpseGuard); + assert(i_player2corpse.find(corpse->GetOwnerGUID()) == i_player2corpse.end()); + i_player2corpse[corpse->GetOwnerGUID()] = corpse; + + // build mapid*cellid -> guid_set map + CellPair cell_pair = MaNGOS::ComputeCellPair(corpse->GetPositionX(), corpse->GetPositionY()); + uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord; + + objmgr.AddCorpseCellData(corpse->GetMapId(),cell_id,corpse->GetOwnerGUID(),corpse->GetInstanceId()); +} + +void +ObjectAccessor::AddCorpsesToGrid(GridPair const& gridpair,GridType& grid,Map* map) +{ + Guard guard(i_corpseGuard); + for(Player2CorpsesMapType::iterator iter = i_player2corpse.begin(); iter != i_player2corpse.end(); ++iter) + if(iter->second->GetGrid()==gridpair) + { + // verify, if the corpse in our instance (add only corpses which are) + if (map->Instanceable()) + { + if (iter->second->GetInstanceId() == map->GetInstanceId()) + { + grid.AddWorldObject(iter->second,iter->second->GetGUID()); + } + } + else + { + grid.AddWorldObject(iter->second,iter->second->GetGUID()); + } + } +} + +Corpse* +ObjectAccessor::ConvertCorpseForPlayer(uint64 player_guid) +{ + Corpse *corpse = GetCorpseForPlayerGUID(player_guid); + if(!corpse) + { + //in fact this function is called from several places + //even when player doesn't have a corpse, not an error + //sLog.outError("ERROR: Try remove corpse that not in map for GUID %ul", player_guid); + return NULL; + } + + DEBUG_LOG("Deleting Corpse and spawning bones.\n"); + + // remove corpse from player_guid -> corpse map + RemoveCorpse(corpse); + + // remove resurrectble corpse from grid object registry (loaded state checked into call) + // do not load the map if it's not loaded + Map *map = MapManager::Instance().FindMap(corpse->GetMapId(), corpse->GetInstanceId()); + if(map) map->Remove(corpse,false); + + // remove corpse from DB + corpse->DeleteFromDB(); + + Corpse *bones = NULL; + // create the bones only if the map and the grid is loaded at the corpse's location + if(map && !map->IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY())) + { + // Create bones, don't change Corpse + bones = new Corpse; + bones->Create(corpse->GetGUIDLow()); + + for (int i = 3; i < CORPSE_END; i++) // don't overwrite guid and object type + bones->SetUInt32Value(i, corpse->GetUInt32Value(i)); + + bones->SetGrid(corpse->GetGrid()); + // bones->m_time = m_time; // don't overwrite time + // bones->m_inWorld = m_inWorld; // don't overwrite world state + // bones->m_type = m_type; // don't overwrite type + bones->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation()); + bones->SetMapId(corpse->GetMapId()); + bones->SetInstanceId(corpse->GetInstanceId()); + + bones->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES); + bones->SetUInt64Value(CORPSE_FIELD_OWNER, 0); + + for (int i = 0; i < EQUIPMENT_SLOT_END; i++) + { + if(corpse->GetUInt32Value(CORPSE_FIELD_ITEM + i)) + bones->SetUInt32Value(CORPSE_FIELD_ITEM + i, 0); + } + + // add bones in grid store if grid loaded where corpse placed + map->Add(bones); + } + + // all references to the corpse should be removed at this point + delete corpse; + + return bones; +} + +void +ObjectAccessor::Update(uint32 diff) +{ + { + typedef std::multimap CreatureLocationHolderType; + CreatureLocationHolderType creature_locations; + //TODO: Player guard + HashMapHolder::MapType& playerMap = HashMapHolder::GetContainer(); + for(HashMapHolder::MapType::iterator iter = playerMap.begin(); iter != playerMap.end(); ++iter) + { + if(iter->second->IsInWorld()) + { + iter->second->Update(diff); + creature_locations.insert( CreatureLocationHolderType::value_type(iter->second->GetMapId(), iter->second) ); + } + } + + Map *map; + + MaNGOS::ObjectUpdater updater(diff); + // for creature + TypeContainerVisitor grid_object_update(updater); + // for pets + TypeContainerVisitor world_object_update(updater); + + for(CreatureLocationHolderType::iterator iter=creature_locations.begin(); iter != creature_locations.end(); ++iter) + { + MapManager::Instance().GetMap((*iter).first, (*iter).second)->resetMarkedCells(); + } + + for(CreatureLocationHolderType::iterator iter=creature_locations.begin(); iter != creature_locations.end(); ++iter) + { + Player *player = (*iter).second; + map = MapManager::Instance().GetMap((*iter).first, player); + + CellPair standing_cell(MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY())); + + // Check for correctness of standing_cell, it also avoids problems with update_cell + if (standing_cell.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || standing_cell.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP) + continue; + + // the overloaded operators handle range checking + // so ther's no need for range checking inside the loop + CellPair begin_cell(standing_cell), end_cell(standing_cell); + begin_cell << 1; begin_cell -= 1; // upper left + end_cell >> 1; end_cell += 1; // lower right + + for(uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; x++) + { + for(uint32 y = begin_cell.y_coord; y <= end_cell.y_coord; y++) + { + uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x; + if( !map->isCellMarked(cell_id) ) + { + CellPair cell_pair(x,y); + map->markCell(cell_id); + Cell cell(cell_pair); + cell.data.Part.reserved = CENTER_DISTRICT; + cell.SetNoCreate(); + CellLock cell_lock(cell, cell_pair); + cell_lock->Visit(cell_lock, grid_object_update, *map); + cell_lock->Visit(cell_lock, world_object_update, *map); + } + } + } + } + } + + _update(); +} + +bool +ObjectAccessor::PlayersNearGrid(uint32 x, uint32 y, uint32 m_id, uint32 i_id) const +{ + CellPair cell_min(x*MAX_NUMBER_OF_CELLS, y*MAX_NUMBER_OF_CELLS); + CellPair cell_max(cell_min.x_coord + MAX_NUMBER_OF_CELLS, cell_min.y_coord+MAX_NUMBER_OF_CELLS); + cell_min << 2; + cell_min -= 2; + cell_max >> 2; + cell_max += 2; + + //TODO: Guard player + HashMapHolder::MapType& playerMap = HashMapHolder::GetContainer(); + for(HashMapHolder::MapType::const_iterator iter=playerMap.begin(); iter != playerMap.end(); ++iter) + { + if( m_id != iter->second->GetMapId() || i_id != iter->second->GetInstanceId() ) + continue; + + CellPair p = MaNGOS::ComputeCellPair(iter->second->GetPositionX(), iter->second->GetPositionY()); + if( (cell_min.x_coord <= p.x_coord && p.x_coord <= cell_max.x_coord) && + (cell_min.y_coord <= p.y_coord && p.y_coord <= cell_max.y_coord) ) + return true; + } + + return false; +} + +void +ObjectAccessor::WorldObjectChangeAccumulator::Visit(PlayerMapType &m) +{ + for(PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter) + if(iter->getSource()->HaveAtClient(&i_object)) + ObjectAccessor::_buildPacket(iter->getSource(), &i_object, i_updateDatas); +} + +void +ObjectAccessor::UpdateObjectVisibility(WorldObject *obj) +{ + CellPair p = MaNGOS::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); + Cell cell(p); + + MapManager::Instance().GetMap(obj->GetMapId(), obj)->UpdateObjectVisibility(obj,cell,p); +} + +void ObjectAccessor::UpdateVisibilityForPlayer( Player* player ) +{ + CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY()); + Cell cell(p); + Map* m = MapManager::Instance().GetMap(player->GetMapId(),player); + + m->UpdatePlayerVisibility(player,cell,p); + m->UpdateObjectsVisibilityFor(player,cell,p); +} + +/// Define the static member of HashMapHolder + +template HM_NAMESPACE::hash_map< uint64, T* > HashMapHolder::m_objectMap; +template ZThread::FastMutex HashMapHolder::i_lock; + +/// Global defintions for the hashmap storage + +template class HashMapHolder; +template class HashMapHolder; +template class HashMapHolder; +template class HashMapHolder; +template class HashMapHolder; +template class HashMapHolder; + +template Player* ObjectAccessor::GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, Player* /*fake*/); +template Pet* ObjectAccessor::GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, Pet* /*fake*/); +template Creature* ObjectAccessor::GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, Creature* /*fake*/); +template Corpse* ObjectAccessor::GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, Corpse* /*fake*/); +template GameObject* ObjectAccessor::GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, GameObject* /*fake*/); +template DynamicObject* ObjectAccessor::GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, DynamicObject* /*fake*/); diff --git a/src/game/ObjectAccessor.h b/src/game/ObjectAccessor.h new file mode 100644 index 00000000000..feb0780e36e --- /dev/null +++ b/src/game/ObjectAccessor.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_OBJECTACCESSOR_H +#define MANGOS_OBJECTACCESSOR_H + +#include "Platform/Define.h" +#include "Policies/Singleton.h" +#include "zthread/FastMutex.h" +#include "Utilities/HashMap.h" +#include "Policies/ThreadingModel.h" + +#include "ByteBuffer.h" +#include "UpdateData.h" + +#include "GridDefines.h" +#include "Object.h" +#include "Player.h" + +#include + +class Creature; +class Corpse; +class Unit; +class GameObject; +class DynamicObject; +class WorldObject; +class Map; + +template +class HashMapHolder +{ + public: + + typedef HM_NAMESPACE::hash_map< uint64, T* > MapType; + typedef ZThread::FastMutex LockType; + typedef MaNGOS::GeneralLock Guard; + + static void Insert(T* o) { m_objectMap[o->GetGUID()] = o; } + + static void Remove(T* o) + { + Guard guard(i_lock); + typename MapType::iterator itr = m_objectMap.find(o->GetGUID()); + if (itr != m_objectMap.end()) + m_objectMap.erase(itr); + } + + static T* Find(uint64 guid) + { + typename MapType::iterator itr = m_objectMap.find(guid); + return (itr != m_objectMap.end()) ? itr->second : NULL; + } + + static MapType& GetContainer() { return m_objectMap; } + + static LockType* GetLock() { return &i_lock; } + private: + + //Non instanciable only static + HashMapHolder() {} + + static LockType i_lock; + static MapType m_objectMap; +}; + +class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton > +{ + + friend class MaNGOS::OperatorNew; + ObjectAccessor(); + ~ObjectAccessor(); + ObjectAccessor(const ObjectAccessor &); + ObjectAccessor& operator=(const ObjectAccessor &); + + public: + typedef HM_NAMESPACE::hash_map Player2CorpsesMapType; + typedef HM_NAMESPACE::hash_map::value_type UpdateDataValueType; + + template static T* GetObjectInWorld(uint64 guid, T* /*fake*/) + { + return HashMapHolder::Find(guid); + } + + static Unit* GetObjectInWorld(uint64 guid, Unit* /*fake*/) + { + if(!guid) + return NULL; + + if (IS_PLAYER_GUID(guid)) + return (Unit*)HashMapHolder::Find(guid); + + if (Unit* u = (Unit*)HashMapHolder::Find(guid)) + return u; + + return (Unit*)HashMapHolder::Find(guid); + } + + template static T* GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, T* /*fake*/) + { + T* obj = HashMapHolder::Find(guid); + if(!obj || obj->GetMapId() != mapid) return NULL; + + CellPair p = MaNGOS::ComputeCellPair(x,y); + if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP ) + { + sLog.outError("ObjectAccessor::GetObjectInWorld: invalid coordinates supplied X:%f Y:%f grid cell [%u:%u]", x, y, p.x_coord, p.y_coord); + return NULL; + } + + CellPair q = MaNGOS::ComputeCellPair(obj->GetPositionX(),obj->GetPositionY()); + if(q.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || q.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP ) + { + sLog.outError("ObjectAccessor::GetObjecInWorld: object "I64FMTD" has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), q.x_coord, q.y_coord); + return NULL; + } + + int32 dx = int32(p.x_coord) - int32(q.x_coord); + int32 dy = int32(p.y_coord) - int32(q.y_coord); + + if (dx > -2 && dx < 2 && dy > -2 && dy < 2) return obj; + else return NULL; + } + + static Object* GetObjectByTypeMask(Player const &, uint64, uint32 typemask); + static Creature* GetNPCIfCanInteractWith(Player const &player, uint64 guid, uint32 npcflagmask); + static Creature* GetCreature(WorldObject const &, uint64); + static Creature* GetCreatureOrPet(WorldObject const &, uint64); + static Unit* GetUnit(WorldObject const &, uint64); + static Pet* GetPet(Unit const &, uint64 guid) { return GetPet(guid); } + static Player* GetPlayer(Unit const &, uint64 guid) { return FindPlayer(guid); } + static GameObject* GetGameObject(WorldObject const &, uint64); + static DynamicObject* GetDynamicObject(Unit const &, uint64); + static Corpse* GetCorpse(WorldObject const &u, uint64 guid); + static Pet* GetPet(uint64 guid); + static Player* FindPlayer(uint64); + + Player* FindPlayerByName(const char *name) ; + + HashMapHolder::MapType& GetPlayers() + { + return HashMapHolder::GetContainer(); + } + + template void AddObject(T *object) + { + HashMapHolder::Insert(object); + } + + template void RemoveObject(T *object) + { + HashMapHolder::Remove(object); + } + + void RemoveObject(Player *pl) + { + HashMapHolder::Remove(pl); + + Guard guard(i_updateGuard); + + std::set::iterator iter2 = std::find(i_objects.begin(), i_objects.end(), (Object *)pl); + if( iter2 != i_objects.end() ) + i_objects.erase(iter2); + } + + void SaveAllPlayers(); + + void AddUpdateObject(Object *obj); + void RemoveUpdateObject(Object *obj); + + void Update(uint32 diff); + + Corpse* GetCorpseForPlayerGUID(uint64 guid); + void RemoveCorpse(Corpse *corpse); + void AddCorpse(Corpse* corpse); + void AddCorpsesToGrid(GridPair const& gridpair,GridType& grid,Map* map); + Corpse* ConvertCorpseForPlayer(uint64 player_guid); + + bool PlayersNearGrid(uint32 x,uint32 y,uint32 m_id,uint32 i_id) const; + + static void UpdateObject(Object* obj, Player* exceptPlayer); + static void _buildUpdateObject(Object* obj, UpdateDataMapType &); + + static void UpdateObjectVisibility(WorldObject* obj); + static void UpdateVisibilityForPlayer(Player* player); + private: + struct WorldObjectChangeAccumulator + { + UpdateDataMapType &i_updateDatas; + WorldObject &i_object; + WorldObjectChangeAccumulator(WorldObject &obj, UpdateDataMapType &d) : i_updateDatas(d), i_object(obj) {} + void Visit(PlayerMapType &); + template void Visit(GridRefManager &) {} + }; + + friend struct WorldObjectChangeAccumulator; + Player2CorpsesMapType i_player2corpse; + + typedef ZThread::FastMutex LockType; + typedef MaNGOS::GeneralLock Guard; + + static void _buildChangeObjectForPlayer(WorldObject *, UpdateDataMapType &); + static void _buildPacket(Player *, Object *, UpdateDataMapType &); + void _update(void); + std::set i_objects; + LockType i_playerGuard; + LockType i_updateGuard; + LockType i_corpseGuard; + LockType i_petGuard; +}; +#endif diff --git a/src/game/ObjectDefines.h b/src/game/ObjectDefines.h new file mode 100644 index 00000000000..ca0fecef5a6 --- /dev/null +++ b/src/game/ObjectDefines.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_OBJECTDEFINES_H +#define MANGOS_OBJECTDEFINES_H + +#include "Platform/Define.h" + +// used for creating values for respawn for example +#define MAKE_PAIR64(l, h) uint64( uint32(l) | ( uint64(h) << 32 ) ) +#define PAIR64_HIPART(x) (uint32)((uint64(x) >> 32) & 0x00000000FFFFFFFFLL) +#define PAIR64_LOPART(x) (uint32)(uint64(x) & 0x00000000FFFFFFFFLL) + +#define MAKE_PAIR32(l, h) uint32( uint16(l) | ( uint32(h) << 16 ) ) +#define PAIR32_HIPART(x) (uint16)((uint32(x) >> 16) & 0x0000FFFF) +#define PAIR32_LOPART(x) (uint16)(uint32(x) & 0x0000FFFF) + +enum HighGuid +{ + HIGHGUID_ITEM = 0x4000, // blizz 4000 + HIGHGUID_CONTAINER = 0x4000, // blizz 4000 + HIGHGUID_PLAYER = 0x0000, // blizz 0000 + HIGHGUID_GAMEOBJECT = 0xF110, // blizz F110 + HIGHGUID_TRANSPORT = 0xF120, // blizz F120 (for GAMEOBJECT_TYPE_TRANSPORT) + HIGHGUID_UNIT = 0xF130, // blizz F130 + HIGHGUID_PET = 0xF140, // blizz F140 + HIGHGUID_DYNAMICOBJECT = 0xF100, // blizz F100 + HIGHGUID_CORPSE = 0xF101, // blizz F100 + HIGHGUID_MO_TRANSPORT = 0x1FC0, // blizz 1FC0 (for GAMEOBJECT_TYPE_MO_TRANSPORT) +}; + +#define IS_EMPTY_GUID(Guid) ( Guid == 0 ) + +#define IS_CREATURE_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_UNIT ) +#define IS_PET_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_PET ) +#define IS_CREATURE_OR_PET_GUID(Guid)( IS_CREATURE_GUID(Guid) || IS_PET_GUID(Guid) ) +#define IS_PLAYER_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_PLAYER && Guid!=0 ) +#define IS_UNIT_GUID(Guid) ( IS_CREATURE_OR_PET_GUID(Guid) || IS_PLAYER_GUID(Guid) ) + // special case for empty guid need check +#define IS_ITEM_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_ITEM ) +#define IS_GAMEOBJECT_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_GAMEOBJECT ) +#define IS_DYNAMICOBJECT_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_DYNAMICOBJECT ) +#define IS_CORPSE_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_CORPSE ) +#define IS_TRANSPORT(Guid) ( GUID_HIPART(Guid) == HIGHGUID_TRANSPORT ) +#define IS_MO_TRANSPORT(Guid) ( GUID_HIPART(Guid) == HIGHGUID_MO_TRANSPORT ) + +// l - OBJECT_FIELD_GUID +// e - OBJECT_FIELD_ENTRY for GO (except GAMEOBJECT_TYPE_MO_TRANSPORT) and creatures or UNIT_FIELD_PETNUMBER for pets +// h - OBJECT_FIELD_GUID + 1 +#define MAKE_NEW_GUID(l, e, h) uint64( uint64(l) | ( uint64(e) << 24 ) | ( uint64(h) << 48 ) ) + +#define GUID_HIPART(x) (uint32)((uint64(x) >> 48) & 0x0000FFFF) + +// We have different low and middle part size for different guid types +#define _GUID_ENPART_2(x) 0 +#define _GUID_ENPART_3(x) (uint32)((uint64(x) >> 24) & 0x0000000000FFFFFFLL) +#define _GUID_LOPART_2(x) (uint32)(uint64(x) & 0x00000000FFFFFFFFLL) +#define _GUID_LOPART_3(x) (uint32)(uint64(x) & 0x0000000000FFFFFFLL) + +inline bool IsGuidHaveEnPart(uint64 const& guid) +{ + switch(GUID_HIPART(guid)) + { + case HIGHGUID_ITEM: + case HIGHGUID_PLAYER: + case HIGHGUID_DYNAMICOBJECT: + case HIGHGUID_CORPSE: + return false; + case HIGHGUID_GAMEOBJECT: + case HIGHGUID_TRANSPORT: + case HIGHGUID_UNIT: + case HIGHGUID_PET: + case HIGHGUID_MO_TRANSPORT: + default: + return true; + } +} + +#define GUID_ENPART(x) (IsGuidHaveEnPart(x) ? _GUID_ENPART_3(x) : _GUID_ENPART_2(x)) +#define GUID_LOPART(x) (IsGuidHaveEnPart(x) ? _GUID_LOPART_3(x) : _GUID_LOPART_2(x)) + +inline char const* GetLogNameForGuid(uint64 guid) +{ + switch(GUID_HIPART(guid)) + { + case HIGHGUID_ITEM: return "item"; + case HIGHGUID_PLAYER: return guid ? "player" : "none"; + case HIGHGUID_GAMEOBJECT: return "gameobject"; + case HIGHGUID_TRANSPORT: return "transport"; + case HIGHGUID_UNIT: return "creature"; + case HIGHGUID_PET: return "pet"; + case HIGHGUID_DYNAMICOBJECT:return "dynobject"; + case HIGHGUID_CORPSE: return "corpse"; + case HIGHGUID_MO_TRANSPORT: return "mo_transport"; + default: + return ""; + } +} +#endif diff --git a/src/game/ObjectGridLoader.cpp b/src/game/ObjectGridLoader.cpp new file mode 100644 index 00000000000..2ce17d4650f --- /dev/null +++ b/src/game/ObjectGridLoader.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ObjectGridLoader.h" +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "MapManager.h" +#include "Creature.h" +#include "GameObject.h" +#include "DynamicObject.h" +#include "Corpse.h" +#include "World.h" +#include "CellImpl.h" + +class MANGOS_DLL_DECL ObjectGridRespawnMover +{ + public: + ObjectGridRespawnMover() {} + + void Move(GridType &grid); + + template void Visit(GridRefManager &) {} + void Visit(CreatureMapType &m); +}; + +void +ObjectGridRespawnMover::Move(GridType &grid) +{ + TypeContainerVisitor mover(*this); + grid.Visit(mover); +} + +void +ObjectGridRespawnMover::Visit(CreatureMapType &m) +{ + // creature in unloading grid can have respawn point in another grid + // if it will be unloaded then it will not respawn in original grid until unload/load original grid + // move to respwn point to prevent this case. For player view in respawn grid this wll be normal respawn. + for(CreatureMapType::iterator iter=m.begin(), next; iter != m.end(); iter = next) + { + next = iter; ++next; + + Creature * c = iter->getSource(); + + assert(!c->isPet() && "ObjectGridRespawnMover don't must be called for pets"); + + Cell const& cur_cell = c->GetCurrentCell(); + + float resp_x, resp_y, resp_z; + c->GetRespawnCoord(resp_x, resp_y, resp_z); + CellPair resp_val = MaNGOS::ComputeCellPair(resp_x, resp_y); + Cell resp_cell(resp_val); + + if(cur_cell.DiffGrid(resp_cell)) + { + MapManager::Instance().GetMap(c->GetMapId(), c)->CreatureRespawnRelocation(c); + // false result ignored: will be unload with other creatures at grid + } + } +} + +// for loading world object at grid loading (Corpses) +class ObjectWorldLoader +{ + public: + explicit ObjectWorldLoader(ObjectGridLoader& gloader) + : i_cell(gloader.i_cell), i_grid(gloader.i_grid), i_map(gloader.i_map), i_corpses (0) + {} + + void Visit(CorpseMapType &m); + + template void Visit(GridRefManager&) { } + + private: + Cell i_cell; + NGridType &i_grid; + Map* i_map; + public: + uint32 i_corpses; +}; + +template void addUnitState(T* /*obj*/, CellPair const& /*cell_pair*/) +{ +} + +template<> void addUnitState(Creature *obj, CellPair const& cell_pair) +{ + Cell cell(cell_pair); + + obj->SetCurrentCell(cell); + if(obj->isSpiritService()) + obj->setDeathState(DEAD); +} + +template +void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager &m, uint32 &count, Map* map) +{ + for(CellGuidSet::const_iterator i_guid = guid_set.begin(); i_guid != guid_set.end(); ++i_guid) + { + T* obj = new T; + uint32 guid = *i_guid; + //sLog.outString("DEBUG: LoadHelper from table: %s for (guid: %u) Loading",table,guid); + if(!obj->LoadFromDB(guid, map)) + { + delete obj; + continue; + } + + obj->GetGridRef().link(&m, obj); + + addUnitState(obj,cell); + obj->AddToWorld(); + ++count; + + } +} + +void LoadHelper(CellCorpseSet const& cell_corpses, CellPair &cell, CorpseMapType &m, uint32 &count, Map* map) +{ + if(cell_corpses.empty()) + return; + + for(CellCorpseSet::const_iterator itr = cell_corpses.begin(); itr != cell_corpses.end(); ++itr) + { + if(itr->second != map->GetInstanceId()) + continue; + + uint32 player_guid = itr->first; + + Corpse *obj = ObjectAccessor::Instance().GetCorpseForPlayerGUID(player_guid); + if(!obj) + continue; + + obj->GetGridRef().link(&m, obj); + + addUnitState(obj,cell); + obj->AddToWorld(); + ++count; + } +} + +void +ObjectGridLoader::Visit(GameObjectMapType &m) +{ + uint32 x = (i_cell.GridX()*MAX_NUMBER_OF_CELLS) + i_cell.CellX(); + uint32 y = (i_cell.GridY()*MAX_NUMBER_OF_CELLS) + i_cell.CellY(); + CellPair cell_pair(x,y); + uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord; + + CellObjectGuids const& cell_guids = objmgr.GetCellObjectGuids(i_map->GetId(), i_map->GetSpawnMode(), cell_id); + + LoadHelper(cell_guids.gameobjects, cell_pair, m, i_gameObjects, i_map); +} + +void +ObjectGridLoader::Visit(CreatureMapType &m) +{ + uint32 x = (i_cell.GridX()*MAX_NUMBER_OF_CELLS) + i_cell.CellX(); + uint32 y = (i_cell.GridY()*MAX_NUMBER_OF_CELLS) + i_cell.CellY(); + CellPair cell_pair(x,y); + uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord; + + CellObjectGuids const& cell_guids = objmgr.GetCellObjectGuids(i_map->GetId(), i_map->GetSpawnMode(), cell_id); + + LoadHelper(cell_guids.creatures, cell_pair, m, i_creatures, i_map); +} + +void +ObjectWorldLoader::Visit(CorpseMapType &m) +{ + uint32 x = (i_cell.GridX()*MAX_NUMBER_OF_CELLS) + i_cell.CellX(); + uint32 y = (i_cell.GridY()*MAX_NUMBER_OF_CELLS) + i_cell.CellY(); + CellPair cell_pair(x,y); + uint32 cell_id = (cell_pair.y_coord*TOTAL_NUMBER_OF_CELLS_PER_MAP) + cell_pair.x_coord; + + // corpses are always added to spawn mode 0 and they are spawned by their instance id + CellObjectGuids const& cell_guids = objmgr.GetCellObjectGuids(i_map->GetId(), 0, cell_id); + LoadHelper(cell_guids.corpses, cell_pair, m, i_corpses, i_map); +} + +void +ObjectGridLoader::Load(GridType &grid) +{ + { + TypeContainerVisitor loader(*this); + grid.Visit(loader); + } + + { + ObjectWorldLoader wloader(*this); + TypeContainerVisitor loader(wloader); + grid.Visit(loader); + i_corpses = wloader.i_corpses; + } +} + +void ObjectGridLoader::LoadN(void) +{ + i_gameObjects = 0; i_creatures = 0; i_corpses = 0; + i_cell.data.Part.cell_y = 0; + for(unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x) + { + i_cell.data.Part.cell_x = x; + for(unsigned int y=0; y < MAX_NUMBER_OF_CELLS; ++y) + { + i_cell.data.Part.cell_y = y; + GridLoader loader; + loader.Load(i_grid(x, y), *this); + } + } + sLog.outDebug("%u GameObjects, %u Creatures, and %u Corpses/Bones loaded for grid %u on map %u", i_gameObjects, i_creatures, i_corpses,i_grid.GetGridId(), i_map->GetId()); +} + +void ObjectGridUnloader::MoveToRespawnN() +{ + for(unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x) + { + for(unsigned int y=0; y < MAX_NUMBER_OF_CELLS; ++y) + { + ObjectGridRespawnMover mover; + mover.Move(i_grid(x, y)); + } + } +} + +void +ObjectGridUnloader::Unload(GridType &grid) +{ + TypeContainerVisitor unloader(*this); + grid.Visit(unloader); +} + +template +void +ObjectGridUnloader::Visit(GridRefManager &m) +{ + while(!m.isEmpty()) + { + T *obj = m.getFirst()->getSource(); + // if option set then object already saved at this moment + if(!sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY)) + obj->SaveRespawnTime(); + ///- object must be out of world before delete + obj->RemoveFromWorld(); + ///- object will get delinked from the manager when deleted + delete obj; + } +} + +template<> +void +ObjectGridUnloader::Visit(CreatureMapType &m) +{ + // remove all cross-reference before deleting + for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + iter->getSource()->CleanupsBeforeDelete(); + + while(!m.isEmpty()) + { + Creature *obj = m.getFirst()->getSource(); + // if option set then object already saved at this moment + if(!sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY)) + obj->SaveRespawnTime(); + ///- object will get delinked from the manager when deleted + delete obj; + } +} + +void +ObjectGridStoper::Stop(GridType &grid) +{ + TypeContainerVisitor stoper(*this); + grid.Visit(stoper); +} + +void +ObjectGridStoper::Visit(CreatureMapType &m) +{ + // stop any fights at grid de-activation and remove dynobjects created at cast by creatures + for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter) + { + iter->getSource()->CombatStop(); + iter->getSource()->DeleteThreatList(); + iter->getSource()->RemoveAllDynObjects(); + } +} + +template void ObjectGridUnloader::Visit(GameObjectMapType &); +template void ObjectGridUnloader::Visit(DynamicObjectMapType &); diff --git a/src/game/ObjectGridLoader.h b/src/game/ObjectGridLoader.h new file mode 100644 index 00000000000..26cc3543b60 --- /dev/null +++ b/src/game/ObjectGridLoader.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_OBJECTGRIDLOADER_H +#define MANGOS_OBJECTGRIDLOADER_H + +#include "Utilities/TypeList.h" +#include "Platform/Define.h" +#include "GameSystem/GridLoader.h" +#include "GridDefines.h" +#include "Cell.h" + +class ObjectWorldLoader; + +class MANGOS_DLL_DECL ObjectGridLoader +{ + friend class ObjectWorldLoader; + + public: + ObjectGridLoader(NGridType &grid, Map* map, const Cell &cell) + : i_cell(cell), i_grid(grid), i_map(map), i_gameObjects(0), i_creatures(0), i_corpses (0) + {} + + void Load(GridType &grid); + void Visit(GameObjectMapType &m); + void Visit(CreatureMapType &m); + void Visit(CorpseMapType &) {} + + void Visit(DynamicObjectMapType&) { } + + void LoadN(void); + + private: + Cell i_cell; + NGridType &i_grid; + Map* i_map; + uint32 i_gameObjects; + uint32 i_creatures; + uint32 i_corpses; +}; + +class MANGOS_DLL_DECL ObjectGridUnloader +{ + public: + ObjectGridUnloader(NGridType &grid) : i_grid(grid) {} + + void MoveToRespawnN(); + void UnloadN() + { + for(unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x) + { + for(unsigned int y=0; y < MAX_NUMBER_OF_CELLS; ++y) + { + GridLoader loader; + loader.Unload(i_grid(x, y), *this); + } + } + } + + void Unload(GridType &grid); + template void Visit(GridRefManager &m); + private: + NGridType &i_grid; +}; + +class MANGOS_DLL_DECL ObjectGridStoper +{ + public: + ObjectGridStoper(NGridType &grid) : i_grid(grid) {} + + void MoveToRespawnN(); + void StopN() + { + for(unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x) + { + for(unsigned int y=0; y < MAX_NUMBER_OF_CELLS; ++y) + { + GridLoader loader; + loader.Stop(i_grid(x, y), *this); + } + } + } + + void Stop(GridType &grid); + void Visit(CreatureMapType &m); + + template void Visit(GridRefManager &) {} + private: + NGridType &i_grid; +}; + +typedef GridLoader GridLoaderType; +#endif diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp new file mode 100644 index 00000000000..982ae3c4bc4 --- /dev/null +++ b/src/game/ObjectMgr.cpp @@ -0,0 +1,6512 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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; +} + +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; +} + +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 = objmgr.GetAItem(auction->item_guidlow); + if(!pItem) + return; + + uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER); + Player *bidder = objmgr.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 + objmgr.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()); + objmgr.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 = objmgr.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 = objmgr.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 = objmgr.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 = objmgr.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 + objmgr.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()); + objmgr.RemoveAItem(pItem->GetGUIDLow()); // we have to remove the item, before we delete it !! + delete pItem; + } +} + +CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id) +{ + return sCreatureStorage.LookupEntry(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 heroicEntries; // already loaded heroic value in creatures + std::set hasHeroicEntries; // already loaded creatures with heroic entry values + + // check data correctness + for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i) + { + CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(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(cInfo->DisplayID_A); + if (!minfo) + sLog.outErrorDb("Creature (Entry: %u) has non-existing modelId_A (%u)", cInfo->Entry, cInfo->DisplayID_A); + minfo = sCreatureModelStorage.LookupEntry(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(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL; + } + + if(cInfo->baseattacktime == 0) + const_cast(cInfo)->baseattacktime = BASE_ATTACK_TIME; + + if(cInfo->rangeattacktime == 0) + const_cast(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(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(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(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(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 val; + s=p=(char*)reinterpret_cast(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(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(addon->auras) = new CreatureDataAddonAura[val.size()/2+1]; + + int i=0; + for(int j=0;j(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(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(i); + if(!addon) + continue; + + ConvertCreatureAddonAuras(const_cast(addon), "creature_template_addon", "Entry"); + + if(!sCreatureStorage.LookupEntry(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(i); + if(!addon) + continue; + + ConvertCreatureAddonAuras(const_cast(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(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(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 heroicCreatures; + for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i) + if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry(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(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(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(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(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(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(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(proto)->BuyCount = 1; + } + + if(proto->InventoryType >= MAX_INVTYPE) + { + sLog.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)",i,proto->InventoryType); + const_cast(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(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(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(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(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(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(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(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(proto)->Spells[0].SpellId = 0; + const_cast(proto)->Spells[0].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE; + const_cast(proto)->Spells[1].SpellId = 0; + const_cast(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(proto)->Spells[0].SpellId = 0; + const_cast(proto)->Spells[1].SpellId = 0; + const_cast(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(proto)->Spells[0].SpellId = 0; + const_cast(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(proto)->Spells[0].SpellId = 0; + const_cast(proto)->Spells[1].SpellId = 0; + const_cast(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(proto)->Spells[0].SpellId = 0; + const_cast(proto)->Spells[1].SpellId = 0; + const_cast(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(proto)->Spells[j].SpellId = 0; + const_cast(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(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(proto)->Spells[j].SpellId = 0; + const_cast(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(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(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(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(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(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(proto)->RandomSuffix = 0; + } + + if(proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet)) + { + sLog.outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)",i,proto->ItemSet); + const_cast(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(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(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 = objmgr.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(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 + { + // 0 1 2 3 + QueryResult *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 `playercreateinfo_spell` 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_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 + QueryResult *result = WorldDatabase.Query("SELECT entry, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue," + // 8 9 10 11 12 13 14 15 + "RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime," + // 16 17 18 19 20 21 22 23 24 25 + "QuestFlags, SpecialFlags, CharTitleId, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell," + // 26 27 28 29 30 31 32 33 34 35 + "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4," + // 36 37 38 39 40 41 42 43 + "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4," + // 44 45 46 47 48 49 50 51 52 53 54 55 + "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, ReqSourceRef1, ReqSourceRef2, ReqSourceRef3, ReqSourceRef4," + // 56 57 58 59 60 61 62 63 + "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4," + // 64 65 66 67 + "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4," + // 68 69 70 71 72 73 + "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6," + // 74 75 76 77 78 79 + "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6," + // 80 81 82 83 84 85 86 87 + "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4," + // 88 89 90 91 92 93 94 95 96 97 + "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5," + // 98 99 100 101 102 103 104 105 106 107 + "RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt," + // 108 109 110 111 112 113 114 115 116 117 + "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4,IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4," + // 118 119 + "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->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(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(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(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(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(-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(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(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(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(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)->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(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 = objmgr.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)->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(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() +{ + objmgr.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() +{ + objmgr.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() +{ + objmgr.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() +{ + objmgr.LoadScripts(sEventScripts, "event_scripts"); + + std::set evt_scripts; + // Load all possible script entries from gameobjects + for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i) + { + GameObjectInfo const * goInfo = sGOStorage.LookupEntry(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::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(i); + if(!page) + continue; + + if(page->Next_Page && !sPageTextStore.LookupEntry(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 checkedPages; + for(PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry(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::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(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= 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 = objmgr.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::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)->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 = objmgr.GetCreatureTemplate(mount_entry); + if(ci) + mount_id = ci->DisplayID_A; + } + if (team == HORDE) + { + mount_entry = node->horde_mount_type; + CreatureInfo const *ci = objmgr.GetCreatureTemplate(mount_entry); + if(ci) + mount_id = ci->DisplayID_H; + } + } + + CreatureModelInfo const *minfo = objmgr.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& 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 = objmgr.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 = objmgr.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 = objmgr.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 = objmgr.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(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(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(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(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(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(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 & list0 = PetHalfName0[entry]; + std::vector & list1 = PetHalfName1[entry]; + + if(list0.empty() || list1.empty()) + { + CreatureInfo const *cinfo = objmgr.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.reputration_max_cap1 = fields[4].GetUInt32(); + repOnKill.repvalue1 = fields[5].GetInt32(); + repOnKill.is_teamaward2 = fields[6].GetBool(); + repOnKill.reputration_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(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, bool positive_entries) +{ + // cleanup affected map part for reloading case + for(MangosStringLocaleMap::iterator itr = mMangosStringLocaleMap.begin(); itr != mMangosStringLocaleMap.end();) + { + if(itr->first > 0 && positive_entries || itr->first < 0 && !positive_entries) + { + 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(positive_entries) // 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 mangos strings. DB table `%s` is empty.",table); + return false; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + int32 entry = fields[0].GetInt32(); + + if(entry==0) + { + sLog.outString("Table `%s` contain reserved entry 0, ignored.",table); + continue; + } + else if(entry < 0) + { + if(positive_entries) + { + sLog.outString("Table `%s` contain unexpected negative entry %i, ignored.",table,entry); + continue; + } + } + else + { + if(!positive_entries) + { + sLog.outString("Table `%s` contain unexpected positive entry %i, ignored.",table,entry); + continue; + } + } + + MangosStringLocale& data = mMangosStringLocaleMap[entry]; + + if(data.Content.size() < 1) + data.Content.resize(1); + + // 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(); + sLog.outString( ">> Loaded %u MaNGOS strings from table %s", mMangosStringLocaleMap.size(),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 ""; +} + +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; + } +} + +const char* GetAreaTriggerScriptNameById(uint32 id) +{ + return objmgr.GetAreaTriggerScriptName(id); +} + + +bool LoadMangosStrings(DatabaseType& db, char const* table) +{ + // for scripting localized strings allowed use _only_ negative entries + return objmgr.LoadMangosStrings(db,table,false); +} diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h new file mode 100644 index 00000000000..f7b7d0a3cca --- /dev/null +++ b/src/game/ObjectMgr.h @@ -0,0 +1,780 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _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 +#include + +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 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 ScriptMap; +typedef std::map 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 CellGuidSet; +typedef std::map CellCorpseSet; +struct CellObjectGuids +{ + CellGuidSet creatures; + CellGuidSet gameobjects; + CellCorpseSet corpses; +}; +typedef HM_NAMESPACE::hash_map CellObjectGuidsMap; +typedef HM_NAMESPACE::hash_map MapObjectGuids; + +typedef HM_NAMESPACE::hash_map RespawnTimes; + +struct MangosStringLocale +{ + std::vector Content; // 0 -> default, i -> i-1 locale index +}; + +typedef HM_NAMESPACE::hash_map CreatureDataMap; +typedef HM_NAMESPACE::hash_map GameObjectDataMap; +typedef HM_NAMESPACE::hash_map CreatureLocaleMap; +typedef HM_NAMESPACE::hash_map GameObjectLocaleMap; +typedef HM_NAMESPACE::hash_map ItemLocaleMap; +typedef HM_NAMESPACE::hash_map QuestLocaleMap; +typedef HM_NAMESPACE::hash_map NpcTextLocaleMap; +typedef HM_NAMESPACE::hash_map PageTextLocaleMap; +typedef HM_NAMESPACE::hash_map MangosStringLocaleMap; + +typedef std::multimap 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 reputration_max_cap1; + int32 repvalue1; + bool is_teamaward2; + uint32 reputration_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 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); + } +}; + +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); + +class PlayerDumpReader; + +class ObjectMgr +{ + friend class PlayerDumpReader; + + public: + ObjectMgr(); + ~ObjectMgr(); + + typedef HM_NAMESPACE::hash_map ItemMap; + + typedef std::set< Group * > GroupSet; + typedef std::set< Guild * > GuildSet; + typedef std::set< ArenaTeam * > ArenaTeamSet; + + typedef HM_NAMESPACE::hash_map QuestMap; + + typedef HM_NAMESPACE::hash_map AreaTriggerMap; + + typedef HM_NAMESPACE::hash_map AreaTriggerScriptMap; + + typedef HM_NAMESPACE::hash_map RepOnKillMap; + + typedef HM_NAMESPACE::hash_map WeatherZoneMap; + + typedef HM_NAMESPACE::hash_map 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(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 ); } + + 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(lowguid); + } + + static CreatureDataAddon const *GetCreatureTemplateAddon( uint32 entry ) + { + return sCreatureInfoAddonStorage.LookupEntry(entry); + } + + static ItemPrototype const* GetItemPrototype(uint32 id) { return sItemStorage.LookupEntry(id); } + + static InstanceTemplate const* GetInstanceTemplate(uint32 map) + { + return sInstanceTemplate.LookupEntry(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& 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, bool positive_entries); + bool LoadMangosStrings() { return LoadMangosStrings(WorldDatabase,"mangos_string",true); } + 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(); + + 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 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); + } + 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 GossipTextMap; + typedef HM_NAMESPACE::hash_map QuestAreaTriggerMap; + typedef HM_NAMESPACE::hash_map BattleMastersMap; + typedef HM_NAMESPACE::hash_map ItemTextMap; + typedef std::set TavernAreaTriggerSet; + typedef std::set 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 ReservedNamesMap; + ReservedNamesMap m_ReservedNames; + + GraveYardMap mGraveYardMap; + + typedef std::vector 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 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 BaseXPMap; // [area level][base xp] + BaseXPMap mBaseXPTable; + + typedef std::map FishingBaseSkillMap; // [areaId][base skill level] + FishingBaseSkillMap mFishingBaseForArea; + + typedef std::map > 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 GuildBankTabPriceMap; + GuildBankTabPriceMap mGuildBankTabPrice; + + // Storage for Conditions. First element (index 0) is reserved for zero-condition (nothing required) + typedef std::vector ConditionStore; + ConditionStore mConditions; + +}; + +#define objmgr MaNGOS::Singleton::Instance() +#endif + +// scripting access functions +bool MANGOS_DLL_SPEC LoadMangosStrings(DatabaseType& db, char const* table); +MANGOS_DLL_SPEC const char* GetAreaTriggerScriptNameById(uint32 id); diff --git a/src/game/ObjectPosSelector.cpp b/src/game/ObjectPosSelector.cpp new file mode 100644 index 00000000000..5936152f9be --- /dev/null +++ b/src/game/ObjectPosSelector.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ObjectPosSelector.h" + +ObjectPosSelector::ObjectPosSelector(float x,float y,float size,float dist) +: m_center_x(x),m_center_y(y),m_size(size),m_dist(dist) +{ + m_anglestep = acos(m_dist/(m_dist+2*m_size)); + + m_nextUsedPos[USED_POS_PLUS] = m_UsedPosLists[USED_POS_PLUS].end(); + m_nextUsedPos[USED_POS_MINUS] = m_UsedPosLists[USED_POS_MINUS].end(); + + m_smallStepAngle[USED_POS_PLUS] = 0; + m_smallStepAngle[USED_POS_MINUS] = 0; + + m_smallStepOk[USED_POS_PLUS] = false; + m_smallStepOk[USED_POS_MINUS] = false; + + m_smallStepNextUsedPos[USED_POS_PLUS] = NULL; + m_smallStepNextUsedPos[USED_POS_MINUS] = NULL; +} + +ObjectPosSelector::UsedPosList::value_type const* ObjectPosSelector::nextUsedPos(UsedPosType uptype) +{ + UsedPosList::const_iterator itr = m_nextUsedPos[uptype]; + if(itr!=m_UsedPosLists[uptype].end()) + ++itr; + + if(itr==m_UsedPosLists[uptype].end()) + { + if(!m_UsedPosLists[~uptype].empty()) + return &*m_UsedPosLists[~uptype].rbegin(); + else + return NULL; + } + else + return &*itr; +} + +void ObjectPosSelector::AddUsedPos(float size,float angle,float dist) +{ + if(angle>=0) + m_UsedPosLists[USED_POS_PLUS].insert(UsedPosList::value_type(angle,UsedPos(1.0,size,dist))); + else + m_UsedPosLists[USED_POS_MINUS].insert(UsedPosList::value_type(-angle,UsedPos(-1.0,size,dist))); +} + +void ObjectPosSelector::InitializeAngle() +{ + m_nextUsedPos[USED_POS_PLUS] = m_UsedPosLists[USED_POS_PLUS].begin(); + m_nextUsedPos[USED_POS_MINUS] = m_UsedPosLists[USED_POS_MINUS].begin(); + + m_smallStepAngle[USED_POS_PLUS] = 0; + m_smallStepAngle[USED_POS_MINUS] = 0; + + m_smallStepOk[USED_POS_PLUS] = true; + m_smallStepOk[USED_POS_MINUS] = true; +} + +bool ObjectPosSelector::FirstAngle(float& angle) +{ + if(m_UsedPosLists[USED_POS_PLUS].empty() && !m_UsedPosLists[USED_POS_MINUS].empty() ) + return NextAngleFor(*m_UsedPosLists[USED_POS_MINUS].begin(),1.0,USED_POS_PLUS,angle); + else if(m_UsedPosLists[USED_POS_MINUS].empty() && !m_UsedPosLists[USED_POS_PLUS].empty() ) + return NextAngleFor(*m_UsedPosLists[USED_POS_PLUS].begin(),-1.0,USED_POS_MINUS,angle); + + return false; +} + +bool ObjectPosSelector::NextAngle(float& angle) +{ + while(m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() || + m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end() || + m_smallStepOk[USED_POS_PLUS] || m_smallStepOk[USED_POS_MINUS] ) + { + // calculate next possible angle + if(NextPosibleAngle(angle)) + return true; + } + + return false; +} + +bool ObjectPosSelector::NextUsedAngle(float& angle) +{ + while(m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() || + m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end() ) + { + // calculate next possible angle + if(!NextPosibleAngle(angle)) + return true; + } + + return false; +} + +bool ObjectPosSelector::NextPosibleAngle( float& angle ) +{ + // ++ direction less updated + if( m_nextUsedPos[USED_POS_PLUS]!=m_UsedPosLists[USED_POS_PLUS].end() && + (m_nextUsedPos[USED_POS_MINUS]==m_UsedPosLists[USED_POS_MINUS].end() || m_nextUsedPos[USED_POS_PLUS]->first <= m_nextUsedPos[USED_POS_MINUS]->first) ) + { + bool ok; + if(m_smallStepOk[USED_POS_PLUS]) + ok = NextSmallStepAngle(1.0,USED_POS_PLUS,angle); + else + ok = NextAngleFor(*m_nextUsedPos[USED_POS_PLUS],1.0,USED_POS_PLUS,angle); + + if(!ok) + ++m_nextUsedPos[USED_POS_PLUS]; // increase. only at fail (original or checked) + return ok; + } + // -- direction less updated + else if( m_nextUsedPos[USED_POS_MINUS]!=m_UsedPosLists[USED_POS_MINUS].end()) + { + bool ok; + if(m_smallStepOk[USED_POS_MINUS]) + ok = NextSmallStepAngle(-1.0,USED_POS_MINUS,angle); + else + ok = NextAngleFor(*m_nextUsedPos[USED_POS_MINUS],-1.0,USED_POS_MINUS,angle); + + if(!ok) + ++m_nextUsedPos[USED_POS_MINUS]; + return ok; + } + else // both list empty + { + if( m_smallStepOk[USED_POS_PLUS] && (!m_smallStepOk[USED_POS_MINUS] || m_smallStepAngle[USED_POS_PLUS] <= m_smallStepAngle[USED_POS_MINUS]) ) + { + return NextSmallStepAngle(1.0,USED_POS_PLUS,angle); + } + // -- direction less updated + else if( m_smallStepOk[USED_POS_MINUS] ) + { + return NextSmallStepAngle(-1.0,USED_POS_MINUS,angle); + } + } + + // no angles + return false; +} diff --git a/src/game/ObjectPosSelector.h b/src/game/ObjectPosSelector.h new file mode 100644 index 00000000000..7423c0a741c --- /dev/null +++ b/src/game/ObjectPosSelector.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _OBJECT_POS_SELECTOR_H +#define _OBJECT_POS_SELECTOR_H + +#include + +#include + +enum UsedPosType { USED_POS_PLUS, USED_POS_MINUS }; + +inline UsedPosType operator ~(UsedPosType uptype) +{ + return uptype==USED_POS_PLUS ? USED_POS_MINUS : USED_POS_PLUS; +} + +struct ObjectPosSelector +{ + struct UsedPos + { + UsedPos(float sign_, float size_,float dist_) : sign(sign_), size(size_),dist(dist_) {} + + float sign; + + float size; // size of point + float dist; // dist to central point (including central point size) + }; + + typedef std::multimap UsedPosList; // abs(angle)->Node + + ObjectPosSelector(float x,float y,float size,float dist); + + void AddUsedPos(float size,float angle,float dist); + void InitializeAngle(); + + bool FirstAngle(float& angle); + bool NextAngle(float& angle); + bool NextUsedAngle(float& angle); + + bool NextPosibleAngle( float& angle ); + + bool CheckAngle(UsedPosList::value_type const& nextUsedPos, float sign, float angle ) const + { + float angle_step2 = GetAngle(nextUsedPos.second); + + float next_angle = nextUsedPos.first; + if(nextUsedPos.second.sign * sign < 0) // last node from diff. list (-pi+alpha) + next_angle = 2*M_PI-next_angle; // move to positive + + return fabs(angle)+angle_step2 <= next_angle; + } + + bool CheckOriginal() const + { + return (m_UsedPosLists[USED_POS_PLUS].empty() || CheckAngle( *m_UsedPosLists[USED_POS_PLUS].begin(),1.0,0)) && + (m_UsedPosLists[USED_POS_MINUS].empty() || CheckAngle( *m_UsedPosLists[USED_POS_MINUS].begin(),-1.0,0)); + } + + bool IsNonBalanced() const { return m_UsedPosLists[USED_POS_PLUS].empty() != m_UsedPosLists[USED_POS_MINUS].empty(); } + + bool NextAngleFor( UsedPosList::value_type const& usedPos, float sign, UsedPosType uptype, float &angle ) + { + float angle_step = GetAngle(usedPos.second); + + // next possible angle + angle = usedPos.first * usedPos.second.sign + angle_step * sign; + + UsedPosList::value_type const* nextNode = nextUsedPos(uptype); + if(nextNode) + { + // if next node permit use selected angle, then do it + if(!CheckAngle(*nextNode, sign, angle)) + { + m_smallStepOk[uptype] = false; + return false; + } + } + + // possible more points + m_smallStepOk[uptype] = true; + m_smallStepAngle[uptype] = angle; + m_smallStepNextUsedPos[uptype] = nextNode; + + return true; + } + + bool NextSmallStepAngle( float sign, UsedPosType uptype, float &angle ) + { + // next possible angle + angle = m_smallStepAngle[uptype] + m_anglestep * sign; + + if(fabs(angle) > M_PI) + { + m_smallStepOk[uptype] = false; + return false; + } + + if(m_smallStepNextUsedPos[uptype]) + { + if(fabs(angle) >= m_smallStepNextUsedPos[uptype]->first) + { + m_smallStepOk[uptype] = false; + return false; + } + + // if next node permit use selected angle, then do it + if(!CheckAngle(*m_smallStepNextUsedPos[uptype], sign, angle)) + { + m_smallStepOk[uptype] = false; + return false; + } + } + + // possible more points + m_smallStepAngle[uptype] = angle; + return true; + } + + // next used post for m_nextUsedPos[uptype] + UsedPosList::value_type const* nextUsedPos(UsedPosType uptype); + + // angle from used pos to next possible free pos + float GetAngle(UsedPos const& usedPos) const { return acos(m_dist/(usedPos.dist+usedPos.size+m_size)); } + + float m_center_x; + float m_center_y; + float m_size; // size of object in center + float m_dist; // distance for searching pos (including central object size) + float m_anglestep; + + UsedPosList m_UsedPosLists[2]; + UsedPosList::const_iterator m_nextUsedPos[2]; + + // field for small step from first after next used pos until next pos + float m_smallStepAngle[2]; + bool m_smallStepOk[2]; + UsedPosList::value_type const* m_smallStepNextUsedPos[2]; +}; +#endif diff --git a/src/game/Opcodes.cpp b/src/game/Opcodes.cpp new file mode 100644 index 00000000000..ed37eca8759 --- /dev/null +++ b/src/game/Opcodes.cpp @@ -0,0 +1,1090 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup u2w +*/ + +#include "Opcodes.h" +#include "WorldSession.h" + +/// Correspondence between opcodes and their names +OpcodeHandler opcodeTable[NUM_MSG_TYPES] = +{ + /*0x000*/ { "MSG_NULL_ACTION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x001*/ { "CMSG_BOOTME", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x002*/ { "CMSG_DBLOOKUP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x003*/ { "SMSG_DBLOOKUP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x004*/ { "CMSG_QUERY_OBJECT_POSITION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x005*/ { "SMSG_QUERY_OBJECT_POSITION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x006*/ { "CMSG_QUERY_OBJECT_ROTATION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x007*/ { "SMSG_QUERY_OBJECT_ROTATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x008*/ { "CMSG_WORLD_TELEPORT", STATUS_LOGGEDIN, &WorldSession::HandleWorldTeleportOpcode }, + /*0x009*/ { "CMSG_TELEPORT_TO_UNIT", STATUS_LOGGEDIN, &WorldSession::Handle_NULL }, + /*0x00A*/ { "CMSG_ZONE_MAP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x00B*/ { "SMSG_ZONE_MAP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x00C*/ { "CMSG_DEBUG_CHANGECELLZONE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x00D*/ { "CMSG_EMBLAZON_TABARD_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x00E*/ { "CMSG_UNEMBLAZON_TABARD_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x00F*/ { "CMSG_RECHARGE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x010*/ { "CMSG_LEARN_SPELL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x011*/ { "CMSG_CREATEMONSTER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x012*/ { "CMSG_DESTROYMONSTER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x013*/ { "CMSG_CREATEITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x014*/ { "CMSG_CREATEGAMEOBJECT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x015*/ { "SMSG_CHECK_FOR_BOTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x016*/ { "CMSG_MAKEMONSTERATTACKGUID", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x017*/ { "CMSG_BOT_DETECTED2", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x018*/ { "CMSG_FORCEACTION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x019*/ { "CMSG_FORCEACTIONONOTHER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x01A*/ { "CMSG_FORCEACTIONSHOW", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x01B*/ { "SMSG_FORCEACTIONSHOW", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x01C*/ { "CMSG_PETGODMODE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x01D*/ { "SMSG_PETGODMODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x01E*/ { "SMSG_DEBUGINFOSPELLMISS_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x01F*/ { "CMSG_WEATHER_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x020*/ { "CMSG_UNDRESSPLAYER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x021*/ { "CMSG_BEASTMASTER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x022*/ { "CMSG_GODMODE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x023*/ { "SMSG_GODMODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x024*/ { "CMSG_CHEAT_SETMONEY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x025*/ { "CMSG_LEVEL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x026*/ { "CMSG_PET_LEVEL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x027*/ { "CMSG_SET_WORLDSTATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x028*/ { "CMSG_COOLDOWN_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x029*/ { "CMSG_USE_SKILL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02A*/ { "CMSG_FLAG_QUEST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02B*/ { "CMSG_FLAG_QUEST_FINISH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02C*/ { "CMSG_CLEAR_QUEST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02D*/ { "CMSG_SEND_EVENT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02E*/ { "CMSG_DEBUG_AISTATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x02F*/ { "SMSG_DEBUG_AISTATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x030*/ { "CMSG_DISABLE_PVP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x031*/ { "CMSG_ADVANCE_SPAWN_TIME", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x032*/ { "CMSG_PVP_PORT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x033*/ { "CMSG_AUTH_SRP6_BEGIN", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x034*/ { "CMSG_AUTH_SRP6_PROOF", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x035*/ { "CMSG_AUTH_SRP6_RECODE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x036*/ { "CMSG_CHAR_CREATE", STATUS_AUTHED, &WorldSession::HandleCharCreateOpcode }, + /*0x037*/ { "CMSG_CHAR_ENUM", STATUS_AUTHED, &WorldSession::HandleCharEnumOpcode }, + /*0x038*/ { "CMSG_CHAR_DELETE", STATUS_AUTHED, &WorldSession::HandleCharDeleteOpcode }, + /*0x039*/ { "SMSG_AUTH_SRP6_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x03A*/ { "SMSG_CHAR_CREATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x03B*/ { "SMSG_CHAR_ENUM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x03C*/ { "SMSG_CHAR_DELETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x03D*/ { "CMSG_PLAYER_LOGIN", STATUS_AUTHED, &WorldSession::HandlePlayerLoginOpcode }, + /*0x03E*/ { "SMSG_NEW_WORLD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x03F*/ { "SMSG_TRANSFER_PENDING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x040*/ { "SMSG_TRANSFER_ABORTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x041*/ { "SMSG_CHARACTER_LOGIN_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x042*/ { "SMSG_LOGIN_SETTIMESPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x043*/ { "SMSG_GAMETIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x044*/ { "CMSG_GAMETIME_SET", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x045*/ { "SMSG_GAMETIME_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x046*/ { "CMSG_GAMESPEED_SET", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x047*/ { "SMSG_GAMESPEED_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x048*/ { "CMSG_SERVERTIME", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x049*/ { "SMSG_SERVERTIME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x04A*/ { "CMSG_PLAYER_LOGOUT", STATUS_LOGGEDIN, &WorldSession::HandlePlayerLogoutOpcode }, + /*0x04B*/ { "CMSG_LOGOUT_REQUEST", STATUS_LOGGEDIN, &WorldSession::HandleLogoutRequestOpcode }, + /*0x04C*/ { "SMSG_LOGOUT_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x04D*/ { "SMSG_LOGOUT_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x04E*/ { "CMSG_LOGOUT_CANCEL", STATUS_LOGGEDIN, &WorldSession::HandleLogoutCancelOpcode }, + /*0x04F*/ { "SMSG_LOGOUT_CANCEL_ACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x050*/ { "CMSG_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleNameQueryOpcode }, + /*0x051*/ { "SMSG_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x052*/ { "CMSG_PET_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePetNameQuery }, + /*0x053*/ { "SMSG_PET_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x054*/ { "CMSG_GUILD_QUERY", STATUS_AUTHED, &WorldSession::HandleGuildQueryOpcode }, + /*0x055*/ { "SMSG_GUILD_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x056*/ { "CMSG_ITEM_QUERY_SINGLE", STATUS_LOGGEDIN, &WorldSession::HandleItemQuerySingleOpcode }, + /*0x057*/ { "CMSG_ITEM_QUERY_MULTIPLE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x058*/ { "SMSG_ITEM_QUERY_SINGLE_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x059*/ { "SMSG_ITEM_QUERY_MULTIPLE_RESPONSE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x05A*/ { "CMSG_PAGE_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePageQueryOpcode }, + /*0x05B*/ { "SMSG_PAGE_TEXT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x05C*/ { "CMSG_QUEST_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleQuestQueryOpcode }, + /*0x05D*/ { "SMSG_QUEST_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x05E*/ { "CMSG_GAMEOBJECT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGameObjectQueryOpcode }, + /*0x05F*/ { "SMSG_GAMEOBJECT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x060*/ { "CMSG_CREATURE_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleCreatureQueryOpcode }, + /*0x061*/ { "SMSG_CREATURE_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x062*/ { "CMSG_WHO", STATUS_LOGGEDIN, &WorldSession::HandleWhoOpcode }, + /*0x063*/ { "SMSG_WHO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x064*/ { "CMSG_WHOIS", STATUS_LOGGEDIN, &WorldSession::HandleWhoisOpcode }, + /*0x065*/ { "SMSG_WHOIS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x066*/ { "CMSG_CONTACT_LIST", STATUS_LOGGEDIN, &WorldSession::HandleFriendListOpcode }, + /*0x067*/ { "SMSG_CONTACT_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x068*/ { "SMSG_FRIEND_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x069*/ { "CMSG_ADD_FRIEND", STATUS_LOGGEDIN, &WorldSession::HandleAddFriendOpcode }, + /*0x06A*/ { "CMSG_DEL_FRIEND", STATUS_LOGGEDIN, &WorldSession::HandleDelFriendOpcode }, + /*0x06B*/ { "CMSG_SET_CONTACT_NOTES", STATUS_LOGGEDIN, &WorldSession::HandleSetFriendNoteOpcode }, + /*0x06C*/ { "CMSG_ADD_IGNORE", STATUS_LOGGEDIN, &WorldSession::HandleAddIgnoreOpcode }, + /*0x06D*/ { "CMSG_DEL_IGNORE", STATUS_LOGGEDIN, &WorldSession::HandleDelIgnoreOpcode }, + /*0x06E*/ { "CMSG_GROUP_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleGroupInviteOpcode }, + /*0x06F*/ { "SMSG_GROUP_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x070*/ { "CMSG_GROUP_CANCEL", STATUS_LOGGEDIN, &WorldSession::Handle_Depricated }, + /*0x071*/ { "SMSG_GROUP_CANCEL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x072*/ { "CMSG_GROUP_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleGroupAcceptOpcode }, + /*0x073*/ { "CMSG_GROUP_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleGroupDeclineOpcode }, + /*0x074*/ { "SMSG_GROUP_DECLINE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x075*/ { "CMSG_GROUP_UNINVITE", STATUS_LOGGEDIN, &WorldSession::HandleGroupUninviteNameOpcode }, + /*0x076*/ { "CMSG_GROUP_UNINVITE_GUID", STATUS_LOGGEDIN, &WorldSession::HandleGroupUninviteGuidOpcode }, + /*0x077*/ { "SMSG_GROUP_UNINVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x078*/ { "CMSG_GROUP_SET_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGroupSetLeaderOpcode }, + /*0x079*/ { "SMSG_GROUP_SET_LEADER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x07A*/ { "CMSG_LOOT_METHOD", STATUS_LOGGEDIN, &WorldSession::HandleLootMethodOpcode }, + /*0x07B*/ { "CMSG_GROUP_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleGroupLeaveOpcode }, + /*0x07C*/ { "SMSG_GROUP_DESTROYED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x07D*/ { "SMSG_GROUP_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x07E*/ { "SMSG_PARTY_MEMBER_STATS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x07F*/ { "SMSG_PARTY_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x080*/ { "UMSG_UPDATE_GROUP_MEMBERS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x081*/ { "CMSG_GUILD_CREATE", STATUS_LOGGEDIN, &WorldSession::HandleGuildCreateOpcode }, + /*0x082*/ { "CMSG_GUILD_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleGuildInviteOpcode }, + /*0x083*/ { "SMSG_GUILD_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x084*/ { "CMSG_GUILD_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleGuildAcceptOpcode }, + /*0x085*/ { "CMSG_GUILD_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleGuildDeclineOpcode }, + /*0x086*/ { "SMSG_GUILD_DECLINE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x087*/ { "CMSG_GUILD_INFO", STATUS_LOGGEDIN, &WorldSession::HandleGuildInfoOpcode }, + /*0x088*/ { "SMSG_GUILD_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x089*/ { "CMSG_GUILD_ROSTER", STATUS_LOGGEDIN, &WorldSession::HandleGuildRosterOpcode }, + /*0x08A*/ { "SMSG_GUILD_ROSTER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x08B*/ { "CMSG_GUILD_PROMOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildPromoteOpcode }, + /*0x08C*/ { "CMSG_GUILD_DEMOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildDemoteOpcode }, + /*0x08D*/ { "CMSG_GUILD_LEAVE", STATUS_LOGGEDIN, &WorldSession::HandleGuildLeaveOpcode }, + /*0x08E*/ { "CMSG_GUILD_REMOVE", STATUS_LOGGEDIN, &WorldSession::HandleGuildRemoveOpcode }, + /*0x08F*/ { "CMSG_GUILD_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleGuildDisbandOpcode }, + /*0x090*/ { "CMSG_GUILD_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGuildLeaderOpcode }, + /*0x091*/ { "CMSG_GUILD_MOTD", STATUS_LOGGEDIN, &WorldSession::HandleGuildMOTDOpcode }, + /*0x092*/ { "SMSG_GUILD_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x093*/ { "SMSG_GUILD_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x094*/ { "UMSG_UPDATE_GUILD", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x095*/ { "CMSG_MESSAGECHAT", STATUS_LOGGEDIN, &WorldSession::HandleMessagechatOpcode }, + /*0x096*/ { "SMSG_MESSAGECHAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x097*/ { "CMSG_JOIN_CHANNEL", STATUS_LOGGEDIN, &WorldSession::HandleChannelJoin }, + /*0x098*/ { "CMSG_LEAVE_CHANNEL", STATUS_LOGGEDIN, &WorldSession::HandleChannelLeave }, + /*0x099*/ { "SMSG_CHANNEL_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x09A*/ { "CMSG_CHANNEL_LIST", STATUS_LOGGEDIN, &WorldSession::HandleChannelList }, + /*0x09B*/ { "SMSG_CHANNEL_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x09C*/ { "CMSG_CHANNEL_PASSWORD", STATUS_LOGGEDIN, &WorldSession::HandleChannelPassword }, + /*0x09D*/ { "CMSG_CHANNEL_SET_OWNER", STATUS_LOGGEDIN, &WorldSession::HandleChannelSetOwner }, + /*0x09E*/ { "CMSG_CHANNEL_OWNER", STATUS_LOGGEDIN, &WorldSession::HandleChannelOwner }, + /*0x09F*/ { "CMSG_CHANNEL_MODERATOR", STATUS_LOGGEDIN, &WorldSession::HandleChannelModerator }, + /*0x0A0*/ { "CMSG_CHANNEL_UNMODERATOR", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnmoderator }, + /*0x0A1*/ { "CMSG_CHANNEL_MUTE", STATUS_LOGGEDIN, &WorldSession::HandleChannelMute }, + /*0x0A2*/ { "CMSG_CHANNEL_UNMUTE", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnmute }, + /*0x0A3*/ { "CMSG_CHANNEL_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleChannelInvite }, + /*0x0A4*/ { "CMSG_CHANNEL_KICK", STATUS_LOGGEDIN, &WorldSession::HandleChannelKick }, + /*0x0A5*/ { "CMSG_CHANNEL_BAN", STATUS_LOGGEDIN, &WorldSession::HandleChannelBan }, + /*0x0A6*/ { "CMSG_CHANNEL_UNBAN", STATUS_LOGGEDIN, &WorldSession::HandleChannelUnban }, + /*0x0A7*/ { "CMSG_CHANNEL_ANNOUNCEMENTS", STATUS_LOGGEDIN, &WorldSession::HandleChannelAnnounce }, + /*0x0A8*/ { "CMSG_CHANNEL_MODERATE", STATUS_LOGGEDIN, &WorldSession::HandleChannelModerate }, + /*0x0A9*/ { "SMSG_UPDATE_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0AA*/ { "SMSG_DESTROY_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0AB*/ { "CMSG_USE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleUseItemOpcode }, + /*0x0AC*/ { "CMSG_OPEN_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleOpenItemOpcode }, + /*0x0AD*/ { "CMSG_READ_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleReadItem }, + /*0x0AE*/ { "SMSG_READ_ITEM_OK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0AF*/ { "SMSG_READ_ITEM_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0B0*/ { "SMSG_ITEM_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0B1*/ { "CMSG_GAMEOBJ_USE", STATUS_LOGGEDIN, &WorldSession::HandleGameObjectUseOpcode }, + /*0x0B2*/ { "CMSG_GAMEOBJ_CHAIR_USE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0B3*/ { "SMSG_GAMEOBJECT_CUSTOM_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0B4*/ { "CMSG_AREATRIGGER", STATUS_LOGGEDIN, &WorldSession::HandleAreaTriggerOpcode }, + /*0x0B5*/ { "MSG_MOVE_START_FORWARD", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0B6*/ { "MSG_MOVE_START_BACKWARD", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0B7*/ { "MSG_MOVE_STOP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0B8*/ { "MSG_MOVE_START_STRAFE_LEFT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0B9*/ { "MSG_MOVE_START_STRAFE_RIGHT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BA*/ { "MSG_MOVE_STOP_STRAFE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BB*/ { "MSG_MOVE_JUMP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BC*/ { "MSG_MOVE_START_TURN_LEFT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BD*/ { "MSG_MOVE_START_TURN_RIGHT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BE*/ { "MSG_MOVE_STOP_TURN", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0BF*/ { "MSG_MOVE_START_PITCH_UP", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0C0*/ { "MSG_MOVE_START_PITCH_DOWN", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0C1*/ { "MSG_MOVE_STOP_PITCH", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0C2*/ { "MSG_MOVE_SET_RUN_MODE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0C3*/ { "MSG_MOVE_SET_WALK_MODE", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0C4*/ { "MSG_MOVE_TOGGLE_LOGGING", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0C5*/ { "MSG_MOVE_TELEPORT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0C6*/ { "MSG_MOVE_TELEPORT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0C7*/ { "MSG_MOVE_TELEPORT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveTeleportAck }, + /*0x0C8*/ { "MSG_MOVE_TOGGLE_FALL_LOGGING", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0C9*/ { "MSG_MOVE_FALL_LAND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0CA*/ { "MSG_MOVE_START_SWIM", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0CB*/ { "MSG_MOVE_STOP_SWIM", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0CC*/ { "MSG_MOVE_SET_RUN_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0CD*/ { "MSG_MOVE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0CE*/ { "MSG_MOVE_SET_RUN_BACK_SPEED_CHEAT",STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0CF*/ { "MSG_MOVE_SET_RUN_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D0*/ { "MSG_MOVE_SET_WALK_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D1*/ { "MSG_MOVE_SET_WALK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D2*/ { "MSG_MOVE_SET_SWIM_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D3*/ { "MSG_MOVE_SET_SWIM_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D4*/ { "MSG_MOVE_SET_SWIM_BACK_SPEED_CHEAT",STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D5*/ { "MSG_MOVE_SET_SWIM_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D6*/ { "MSG_MOVE_SET_ALL_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D7*/ { "MSG_MOVE_SET_TURN_RATE_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D8*/ { "MSG_MOVE_SET_TURN_RATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0D9*/ { "MSG_MOVE_TOGGLE_COLLISION_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0DA*/ { "MSG_MOVE_SET_FACING", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0DB*/ { "MSG_MOVE_SET_PITCH", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0DC*/ { "MSG_MOVE_WORLDPORT_ACK", STATUS_TRANSFER_PENDING, &WorldSession::HandleMoveWorldportAckOpcode}, + /*0x0DD*/ { "SMSG_MONSTER_MOVE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0DE*/ { "SMSG_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0DF*/ { "SMSG_MOVE_LAND_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0E0*/ { "MSG_MOVE_SET_RAW_POSITION_ACK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0E1*/ { "CMSG_MOVE_SET_RAW_POSITION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0E2*/ { "SMSG_FORCE_RUN_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0E3*/ { "CMSG_FORCE_RUN_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x0E4*/ { "SMSG_FORCE_RUN_BACK_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0E5*/ { "CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck }, + /*0x0E6*/ { "SMSG_FORCE_SWIM_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0E7*/ { "CMSG_FORCE_SWIM_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x0E8*/ { "SMSG_FORCE_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0E9*/ { "CMSG_FORCE_MOVE_ROOT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveRootAck }, + /*0x0EA*/ { "SMSG_FORCE_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0EB*/ { "CMSG_FORCE_MOVE_UNROOT_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveUnRootAck }, + /*0x0EC*/ { "MSG_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0ED*/ { "MSG_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0EE*/ { "MSG_MOVE_HEARTBEAT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x0EF*/ { "SMSG_MOVE_KNOCK_BACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0F0*/ { "CMSG_MOVE_KNOCK_BACK_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveKnockBackAck }, + /*0x0F1*/ { "MSG_MOVE_KNOCK_BACK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0F2*/ { "SMSG_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0F3*/ { "SMSG_MOVE_NORMAL_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0F4*/ { "SMSG_MOVE_SET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0F5*/ { "SMSG_MOVE_UNSET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0F6*/ { "CMSG_MOVE_HOVER_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveHoverAck }, + /*0x0F7*/ { "MSG_MOVE_HOVER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0F8*/ { "CMSG_TRIGGER_CINEMATIC_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0F9*/ { "CMSG_OPENING_CINEMATIC", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x0FA*/ { "SMSG_TRIGGER_CINEMATIC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0FB*/ { "CMSG_NEXT_CINEMATIC_CAMERA", STATUS_LOGGEDIN, &WorldSession::HandleNextCinematicCamera }, + /*0x0FC*/ { "CMSG_COMPLETE_CINEMATIC", STATUS_LOGGEDIN, &WorldSession::HandleCompleteCinema }, + /*0x0FD*/ { "SMSG_TUTORIAL_FLAGS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x0FE*/ { "CMSG_TUTORIAL_FLAG", STATUS_LOGGEDIN, &WorldSession::HandleTutorialFlag }, + /*0x0FF*/ { "CMSG_TUTORIAL_CLEAR", STATUS_LOGGEDIN, &WorldSession::HandleTutorialClear }, + /*0x100*/ { "CMSG_TUTORIAL_RESET", STATUS_LOGGEDIN, &WorldSession::HandleTutorialReset }, + /*0x101*/ { "CMSG_STANDSTATECHANGE", STATUS_LOGGEDIN, &WorldSession::HandleStandStateChangeOpcode }, + /*0x102*/ { "CMSG_EMOTE", STATUS_LOGGEDIN, &WorldSession::HandleEmoteOpcode }, + /*0x103*/ { "SMSG_EMOTE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x104*/ { "CMSG_TEXT_EMOTE", STATUS_LOGGEDIN, &WorldSession::HandleTextEmoteOpcode }, + /*0x105*/ { "SMSG_TEXT_EMOTE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x106*/ { "CMSG_AUTOEQUIP_GROUND_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x107*/ { "CMSG_AUTOSTORE_GROUND_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x108*/ { "CMSG_AUTOSTORE_LOOT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutostoreLootItemOpcode }, + /*0x109*/ { "CMSG_STORE_LOOT_IN_SLOT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x10A*/ { "CMSG_AUTOEQUIP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoEquipItemOpcode }, + /*0x10B*/ { "CMSG_AUTOSTORE_BAG_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoStoreBagItemOpcode }, + /*0x10C*/ { "CMSG_SWAP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSwapItem }, + /*0x10D*/ { "CMSG_SWAP_INV_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSwapInvItemOpcode }, + /*0x10E*/ { "CMSG_SPLIT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSplitItemOpcode }, + /*0x10F*/ { "CMSG_AUTOEQUIP_ITEM_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleAutoEquipItemSlotOpcode }, + /*0x110*/ { "OBSOLETE_DROP_ITEM", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x111*/ { "CMSG_DESTROYITEM", STATUS_LOGGEDIN, &WorldSession::HandleDestroyItemOpcode }, + /*0x112*/ { "SMSG_INVENTORY_CHANGE_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x113*/ { "SMSG_OPEN_CONTAINER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x114*/ { "CMSG_INSPECT", STATUS_LOGGEDIN, &WorldSession::HandleInspectOpcode }, + /*0x115*/ { "SMSG_INSPECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x116*/ { "CMSG_INITIATE_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleInitiateTradeOpcode }, + /*0x117*/ { "CMSG_BEGIN_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleBeginTradeOpcode }, + /*0x118*/ { "CMSG_BUSY_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleBusyTradeOpcode }, + /*0x119*/ { "CMSG_IGNORE_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleIgnoreTradeOpcode }, + /*0x11A*/ { "CMSG_ACCEPT_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleAcceptTradeOpcode }, + /*0x11B*/ { "CMSG_UNACCEPT_TRADE", STATUS_LOGGEDIN, &WorldSession::HandleUnacceptTradeOpcode }, + /*0x11C*/ { "CMSG_CANCEL_TRADE", STATUS_AUTHED, &WorldSession::HandleCancelTradeOpcode }, + // also send after logout complete + /*0x11D*/ { "CMSG_SET_TRADE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSetTradeItemOpcode }, + /*0x11E*/ { "CMSG_CLEAR_TRADE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleClearTradeItemOpcode }, + /*0x11F*/ { "CMSG_SET_TRADE_GOLD", STATUS_LOGGEDIN, &WorldSession::HandleSetTradeGoldOpcode }, + /*0x120*/ { "SMSG_TRADE_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x121*/ { "SMSG_TRADE_STATUS_EXTENDED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x122*/ { "SMSG_INITIALIZE_FACTIONS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x123*/ { "SMSG_SET_FACTION_VISIBLE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x124*/ { "SMSG_SET_FACTION_STANDING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x125*/ { "CMSG_SET_FACTION_ATWAR", STATUS_LOGGEDIN, &WorldSession::HandleSetFactionAtWar }, + /*0x126*/ { "CMSG_SET_FACTION_CHEAT", STATUS_LOGGEDIN, &WorldSession::HandleSetFactionCheat }, + /*0x127*/ { "SMSG_SET_PROFICIENCY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x128*/ { "CMSG_SET_ACTION_BUTTON", STATUS_LOGGEDIN, &WorldSession::HandleSetActionButtonOpcode }, + /*0x129*/ { "SMSG_ACTION_BUTTONS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x12A*/ { "SMSG_INITIAL_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x12B*/ { "SMSG_LEARNED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x12C*/ { "SMSG_SUPERCEDED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x12D*/ { "CMSG_NEW_SPELL_SLOT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x12E*/ { "CMSG_CAST_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleCastSpellOpcode }, + /*0x12F*/ { "CMSG_CANCEL_CAST", STATUS_LOGGEDIN, &WorldSession::HandleCancelCastOpcode }, + /*0x130*/ { "SMSG_CAST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x131*/ { "SMSG_SPELL_START", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x132*/ { "SMSG_SPELL_GO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x133*/ { "SMSG_SPELL_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x134*/ { "SMSG_SPELL_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x135*/ { "SMSG_COOLDOWN_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x136*/ { "CMSG_CANCEL_AURA", STATUS_LOGGEDIN, &WorldSession::HandleCancelAuraOpcode }, + /*0x137*/ { "SMSG_UPDATE_AURA_DURATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x138*/ { "SMSG_PET_CAST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x139*/ { "MSG_CHANNEL_START", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x13A*/ { "MSG_CHANNEL_UPDATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x13B*/ { "CMSG_CANCEL_CHANNELLING", STATUS_LOGGEDIN, &WorldSession::HandleCancelChanneling }, + /*0x13C*/ { "SMSG_AI_REACTION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x13D*/ { "CMSG_SET_SELECTION", STATUS_LOGGEDIN, &WorldSession::HandleSetSelectionOpcode }, + /*0x13E*/ { "CMSG_SET_TARGET_OBSOLETE", STATUS_LOGGEDIN, &WorldSession::HandleSetTargetOpcode }, + /*0x13F*/ { "CMSG_UNUSED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x140*/ { "CMSG_UNUSED2", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x141*/ { "CMSG_ATTACKSWING", STATUS_LOGGEDIN, &WorldSession::HandleAttackSwingOpcode }, + /*0x142*/ { "CMSG_ATTACKSTOP", STATUS_LOGGEDIN, &WorldSession::HandleAttackStopOpcode }, + /*0x143*/ { "SMSG_ATTACKSTART", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x144*/ { "SMSG_ATTACKSTOP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x145*/ { "SMSG_ATTACKSWING_NOTINRANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x146*/ { "SMSG_ATTACKSWING_BADFACING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x147*/ { "SMSG_ATTACKSWING_NOTSTANDING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x148*/ { "SMSG_ATTACKSWING_DEADTARGET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x149*/ { "SMSG_ATTACKSWING_CANT_ATTACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14A*/ { "SMSG_ATTACKERSTATEUPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14B*/ { "SMSG_VICTIMSTATEUPDATE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14C*/ { "SMSG_DAMAGE_DONE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14D*/ { "SMSG_DAMAGE_TAKEN_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14E*/ { "SMSG_CANCEL_COMBAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x14F*/ { "SMSG_PLAYER_COMBAT_XP_GAIN_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x150*/ { "SMSG_SPELLHEALLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x151*/ { "SMSG_SPELLENERGIZELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x152*/ { "CMSG_SHEATHE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x153*/ { "CMSG_SAVE_PLAYER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x154*/ { "CMSG_SETDEATHBINDPOINT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x155*/ { "SMSG_BINDPOINTUPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x156*/ { "CMSG_GETDEATHBINDZONE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x157*/ { "SMSG_BINDZONEREPLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x158*/ { "SMSG_PLAYERBOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x159*/ { "SMSG_CLIENT_CONTROL_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x15A*/ { "CMSG_REPOP_REQUEST", STATUS_LOGGEDIN, &WorldSession::HandleRepopRequestOpcode }, + /*0x15B*/ { "SMSG_RESURRECT_REQUEST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x15C*/ { "CMSG_RESURRECT_RESPONSE", STATUS_LOGGEDIN, &WorldSession::HandleResurrectResponseOpcode }, + /*0x15D*/ { "CMSG_LOOT", STATUS_LOGGEDIN, &WorldSession::HandleLootOpcode }, + /*0x15E*/ { "CMSG_LOOT_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleLootMoneyOpcode }, + /*0x15F*/ { "CMSG_LOOT_RELEASE", STATUS_LOGGEDIN, &WorldSession::HandleLootReleaseOpcode }, + /*0x160*/ { "SMSG_LOOT_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x161*/ { "SMSG_LOOT_RELEASE_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x162*/ { "SMSG_LOOT_REMOVED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x163*/ { "SMSG_LOOT_MONEY_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x164*/ { "SMSG_LOOT_ITEM_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x165*/ { "SMSG_LOOT_CLEAR_MONEY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x166*/ { "SMSG_ITEM_PUSH_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x167*/ { "SMSG_DUEL_REQUESTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x168*/ { "SMSG_DUEL_OUTOFBOUNDS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x169*/ { "SMSG_DUEL_INBOUNDS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x16A*/ { "SMSG_DUEL_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x16B*/ { "SMSG_DUEL_WINNER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x16C*/ { "CMSG_DUEL_ACCEPTED", STATUS_LOGGEDIN, &WorldSession::HandleDuelAcceptedOpcode }, + /*0x16D*/ { "CMSG_DUEL_CANCELLED", STATUS_LOGGEDIN, &WorldSession::HandleDuelCancelledOpcode }, + /*0x16E*/ { "SMSG_MOUNTRESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x16F*/ { "SMSG_DISMOUNTRESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x170*/ { "SMSG_PUREMOUNT_CANCELLED_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x171*/ { "CMSG_MOUNTSPECIAL_ANIM", STATUS_LOGGEDIN, &WorldSession::HandleMountSpecialAnimOpcode }, + /*0x172*/ { "SMSG_MOUNTSPECIAL_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x173*/ { "SMSG_PET_TAME_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x174*/ { "CMSG_PET_SET_ACTION", STATUS_LOGGEDIN, &WorldSession::HandlePetSetAction }, + /*0x175*/ { "CMSG_PET_ACTION", STATUS_LOGGEDIN, &WorldSession::HandlePetAction }, + /*0x176*/ { "CMSG_PET_ABANDON", STATUS_LOGGEDIN, &WorldSession::HandlePetAbandon }, + /*0x177*/ { "CMSG_PET_RENAME", STATUS_LOGGEDIN, &WorldSession::HandlePetRename }, + /*0x178*/ { "SMSG_PET_NAME_INVALID", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x179*/ { "SMSG_PET_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x17A*/ { "SMSG_PET_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x17B*/ { "CMSG_GOSSIP_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleGossipHelloOpcode }, + /*0x17C*/ { "CMSG_GOSSIP_SELECT_OPTION", STATUS_LOGGEDIN, &WorldSession::HandleGossipSelectOptionOpcode }, + /*0x17D*/ { "SMSG_GOSSIP_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x17E*/ { "SMSG_GOSSIP_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x17F*/ { "CMSG_NPC_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleNpcTextQueryOpcode }, + /*0x180*/ { "SMSG_NPC_TEXT_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x181*/ { "SMSG_NPC_WONT_TALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x182*/ { "CMSG_QUESTGIVER_STATUS_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverStatusQueryOpcode}, + /*0x183*/ { "SMSG_QUESTGIVER_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x184*/ { "CMSG_QUESTGIVER_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverHelloOpcode }, + /*0x185*/ { "SMSG_QUESTGIVER_QUEST_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x186*/ { "CMSG_QUESTGIVER_QUERY_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverQuestQueryOpcode}, + /*0x187*/ { "CMSG_QUESTGIVER_QUEST_AUTOLAUNCH", STATUS_LOGGEDIN, &WorldSession::HandleQuestAutoLaunch }, + /*0x188*/ { "SMSG_QUESTGIVER_QUEST_DETAILS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x189*/ { "CMSG_QUESTGIVER_ACCEPT_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverAcceptQuestOpcode}, + /*0x18A*/ { "CMSG_QUESTGIVER_COMPLETE_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestComplete }, + /*0x18B*/ { "SMSG_QUESTGIVER_REQUEST_ITEMS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x18C*/ { "CMSG_QUESTGIVER_REQUEST_REWARD", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverRequestRewardOpcode}, + /*0x18D*/ { "SMSG_QUESTGIVER_OFFER_REWARD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x18E*/ { "CMSG_QUESTGIVER_CHOOSE_REWARD", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverChooseRewardOpcode}, + /*0x18F*/ { "SMSG_QUESTGIVER_QUEST_INVALID", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x190*/ { "CMSG_QUESTGIVER_CANCEL", STATUS_LOGGEDIN, &WorldSession::HandleQuestgiverCancel }, + /*0x191*/ { "SMSG_QUESTGIVER_QUEST_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x192*/ { "SMSG_QUESTGIVER_QUEST_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x193*/ { "CMSG_QUESTLOG_SWAP_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestLogSwapQuest }, + /*0x194*/ { "CMSG_QUESTLOG_REMOVE_QUEST", STATUS_LOGGEDIN, &WorldSession::HandleQuestLogRemoveQuest }, + /*0x195*/ { "SMSG_QUESTLOG_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x196*/ { "SMSG_QUESTUPDATE_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x197*/ { "SMSG_QUESTUPDATE_FAILEDTIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x198*/ { "SMSG_QUESTUPDATE_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x199*/ { "SMSG_QUESTUPDATE_ADD_KILL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x19A*/ { "SMSG_QUESTUPDATE_ADD_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x19B*/ { "CMSG_QUEST_CONFIRM_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleQuestConfirmAccept }, + /*0x19C*/ { "SMSG_QUEST_CONFIRM_ACCEPT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x19D*/ { "CMSG_PUSHQUESTTOPARTY", STATUS_LOGGEDIN, &WorldSession::HandleQuestPushToParty }, + /*0x19E*/ { "CMSG_LIST_INVENTORY", STATUS_LOGGEDIN, &WorldSession::HandleListInventoryOpcode }, + /*0x19F*/ { "SMSG_LIST_INVENTORY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1A0*/ { "CMSG_SELL_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleSellItemOpcode }, + /*0x1A1*/ { "SMSG_SELL_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1A2*/ { "CMSG_BUY_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleBuyItemOpcode }, + /*0x1A3*/ { "CMSG_BUY_ITEM_IN_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyItemInSlotOpcode }, + /*0x1A4*/ { "SMSG_BUY_ITEM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1A5*/ { "SMSG_BUY_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1A6*/ { "CMSG_TAXICLEARALLNODES", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1A7*/ { "CMSG_TAXIENABLEALLNODES", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1A8*/ { "CMSG_TAXISHOWNODES", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1A9*/ { "SMSG_SHOWTAXINODES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1AA*/ { "CMSG_TAXINODE_STATUS_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleTaxiNodeStatusQueryOpcode }, + /*0x1AB*/ { "SMSG_TAXINODE_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1AC*/ { "CMSG_TAXIQUERYAVAILABLENODES", STATUS_LOGGEDIN, &WorldSession::HandleTaxiQueryAvailableNodesOpcode}, + /*0x1AD*/ { "CMSG_ACTIVATETAXI", STATUS_LOGGEDIN, &WorldSession::HandleActivateTaxiOpcode }, + /*0x1AE*/ { "SMSG_ACTIVATETAXIREPLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1AF*/ { "SMSG_NEW_TAXI_PATH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B0*/ { "CMSG_TRAINER_LIST", STATUS_LOGGEDIN, &WorldSession::HandleTrainerListOpcode }, + /*0x1B1*/ { "SMSG_TRAINER_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B2*/ { "CMSG_TRAINER_BUY_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleTrainerBuySpellOpcode }, + /*0x1B3*/ { "SMSG_TRAINER_BUY_SUCCEEDED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B4*/ { "SMSG_TRAINER_BUY_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B5*/ { "CMSG_BINDER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleBinderActivateOpcode }, + /*0x1B6*/ { "SMSG_PLAYERBINDERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B7*/ { "CMSG_BANKER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleBankerActivateOpcode }, + /*0x1B8*/ { "SMSG_SHOW_BANK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1B9*/ { "CMSG_BUY_BANK_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyBankSlotOpcode }, + /*0x1BA*/ { "SMSG_BUY_BANK_SLOT_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1BB*/ { "CMSG_PETITION_SHOWLIST", STATUS_LOGGEDIN, &WorldSession::HandlePetitionShowListOpcode }, + /*0x1BC*/ { "SMSG_PETITION_SHOWLIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1BD*/ { "CMSG_PETITION_BUY", STATUS_LOGGEDIN, &WorldSession::HandlePetitionBuyOpcode }, + /*0x1BE*/ { "CMSG_PETITION_SHOW_SIGNATURES", STATUS_LOGGEDIN, &WorldSession::HandlePetitionShowSignOpcode }, + /*0x1BF*/ { "SMSG_PETITION_SHOW_SIGNATURES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1C0*/ { "CMSG_PETITION_SIGN", STATUS_LOGGEDIN, &WorldSession::HandlePetitionSignOpcode }, + /*0x1C1*/ { "SMSG_PETITION_SIGN_RESULTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1C2*/ { "MSG_PETITION_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandlePetitionDeclineOpcode }, + /*0x1C3*/ { "CMSG_OFFER_PETITION", STATUS_LOGGEDIN, &WorldSession::HandleOfferPetitionOpcode }, + /*0x1C4*/ { "CMSG_TURN_IN_PETITION", STATUS_LOGGEDIN, &WorldSession::HandleTurnInPetitionOpcode }, + /*0x1C5*/ { "SMSG_TURN_IN_PETITION_RESULTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1C6*/ { "CMSG_PETITION_QUERY", STATUS_LOGGEDIN, &WorldSession::HandlePetitionQueryOpcode }, + /*0x1C7*/ { "SMSG_PETITION_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1C8*/ { "SMSG_FISH_NOT_HOOKED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1C9*/ { "SMSG_FISH_ESCAPED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1CA*/ { "CMSG_BUG", STATUS_LOGGEDIN, &WorldSession::HandleBugOpcode }, + /*0x1CB*/ { "SMSG_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1CC*/ { "CMSG_PLAYED_TIME", STATUS_LOGGEDIN, &WorldSession::HandlePlayedTime }, + /*0x1CD*/ { "SMSG_PLAYED_TIME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1CE*/ { "CMSG_QUERY_TIME", STATUS_LOGGEDIN, &WorldSession::HandleQueryTimeOpcode }, + /*0x1CF*/ { "SMSG_QUERY_TIME_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D0*/ { "SMSG_LOG_XPGAIN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D1*/ { "SMSG_AURACASTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D2*/ { "CMSG_RECLAIM_CORPSE", STATUS_LOGGEDIN, &WorldSession::HandleCorpseReclaimOpcode }, + /*0x1D3*/ { "CMSG_WRAP_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleWrapItemOpcode }, + /*0x1D4*/ { "SMSG_LEVELUP_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D5*/ { "MSG_MINIMAP_PING", STATUS_LOGGEDIN, &WorldSession::HandleMinimapPingOpcode }, + /*0x1D6*/ { "SMSG_RESISTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D7*/ { "SMSG_ENCHANTMENTLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1D8*/ { "CMSG_SET_SKILL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1D9*/ { "SMSG_START_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1DA*/ { "SMSG_PAUSE_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1DB*/ { "SMSG_STOP_MIRROR_TIMER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1DC*/ { "CMSG_PING", STATUS_NEVER, &WorldSession::Handle_EarlyProccess }, + /*0x1DD*/ { "SMSG_PONG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1DE*/ { "SMSG_CLEAR_COOLDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1DF*/ { "SMSG_GAMEOBJECT_PAGETEXT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1E0*/ { "CMSG_SETSHEATHED", STATUS_LOGGEDIN, &WorldSession::HandleSetSheathedOpcode }, + /*0x1E1*/ { "SMSG_COOLDOWN_CHEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1E2*/ { "SMSG_SPELL_DELAYED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1E3*/ { "CMSG_PLAYER_MACRO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1E4*/ { "SMSG_PLAYER_MACRO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1E5*/ { "CMSG_GHOST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1E6*/ { "CMSG_GM_INVIS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1E7*/ { "SMSG_INVALID_PROMOTION_CODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1E8*/ { "MSG_GM_BIND_OTHER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1E9*/ { "MSG_GM_SUMMON", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1EA*/ { "SMSG_ITEM_TIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1EB*/ { "SMSG_ITEM_ENCHANT_TIME_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1EC*/ { "SMSG_AUTH_CHALLENGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1ED*/ { "CMSG_AUTH_SESSION", STATUS_NEVER, &WorldSession::Handle_EarlyProccess }, + /*0x1EE*/ { "SMSG_AUTH_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1EF*/ { "MSG_GM_SHOWLABEL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1F0*/ { "CMSG_PET_CAST_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleAddDynamicTargetObsoleteOpcode}, + /*0x1F1*/ { "MSG_SAVE_GUILD_EMBLEM", STATUS_LOGGEDIN, &WorldSession::HandleGuildSaveEmblemOpcode }, + /*0x1F2*/ { "MSG_TABARDVENDOR_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleTabardVendorActivateOpcode}, + /*0x1F3*/ { "SMSG_PLAY_SPELL_VISUAL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1F4*/ { "CMSG_ZONEUPDATE", STATUS_LOGGEDIN, &WorldSession::HandleZoneUpdateOpcode }, + /*0x1F5*/ { "SMSG_PARTYKILLLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1F6*/ { "SMSG_COMPRESSED_UPDATE_OBJECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1F7*/ { "SMSG_PLAY_SPELL_IMPACT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1F8*/ { "SMSG_EXPLORATION_EXPERIENCE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1F9*/ { "CMSG_GM_SET_SECURITY_GROUP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1FA*/ { "CMSG_GM_NUKE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1FB*/ { "MSG_RANDOM_ROLL", STATUS_LOGGEDIN, &WorldSession::HandleRandomRollOpcode }, + /*0x1FC*/ { "SMSG_ENVIRONMENTALDAMAGELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1FD*/ { "CMSG_RWHOIS_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x1FE*/ { "SMSG_RWHOIS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x1FF*/ { "MSG_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleLookingForGroup }, + /*0x200*/ { "CMSG_SET_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleSetLfgOpcode }, + /*0x201*/ { "CMSG_UNLEARN_SPELL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x202*/ { "CMSG_UNLEARN_SKILL", STATUS_LOGGEDIN, &WorldSession::HandleUnlearnSkillOpcode }, + /*0x203*/ { "SMSG_REMOVED_SPELL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x204*/ { "CMSG_DECHARGE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x205*/ { "CMSG_GMTICKET_CREATE", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketCreateOpcode }, + /*0x206*/ { "SMSG_GMTICKET_CREATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x207*/ { "CMSG_GMTICKET_UPDATETEXT", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketUpdateTextOpcode }, + /*0x208*/ { "SMSG_GMTICKET_UPDATETEXT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x209*/ { "SMSG_ACCOUNT_DATA_TIMES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x20A*/ { "CMSG_REQUEST_ACCOUNT_DATA", STATUS_LOGGEDIN, &WorldSession::HandleRequestAccountData }, + /*0x20B*/ { "CMSG_UPDATE_ACCOUNT_DATA", STATUS_LOGGEDIN, &WorldSession::HandleUpdateAccountData }, + /*0x20C*/ { "SMSG_UPDATE_ACCOUNT_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x20D*/ { "SMSG_CLEAR_FAR_SIGHT_IMMEDIATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x20E*/ { "SMSG_POWERGAINLOG_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x20F*/ { "CMSG_GM_TEACH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x210*/ { "CMSG_GM_CREATE_ITEM_TARGET", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x211*/ { "CMSG_GMTICKET_GETTICKET", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketGetTicketOpcode }, + /*0x212*/ { "SMSG_GMTICKET_GETTICKET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x213*/ { "CMSG_UNLEARN_TALENTS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x214*/ { "SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x215*/ { "SMSG_GAMEOBJECT_DESPAWN_ANIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x216*/ { "MSG_CORPSE_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleCorpseQueryOpcode }, + /*0x217*/ { "CMSG_GMTICKET_DELETETICKET", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketDeleteOpcode }, + /*0x218*/ { "SMSG_GMTICKET_DELETETICKET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x219*/ { "SMSG_CHAT_WRONG_FACTION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x21A*/ { "CMSG_GMTICKET_SYSTEMSTATUS", STATUS_LOGGEDIN, &WorldSession::HandleGMTicketSystemStatusOpcode}, + /*0x21B*/ { "SMSG_GMTICKET_SYSTEMSTATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x21C*/ { "CMSG_SPIRIT_HEALER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleSpiritHealerActivateOpcode}, + /*0x21D*/ { "CMSG_SET_STAT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x21E*/ { "SMSG_SET_REST_START", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x21F*/ { "CMSG_SKILL_BUY_STEP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x220*/ { "CMSG_SKILL_BUY_RANK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x221*/ { "CMSG_XP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x222*/ { "SMSG_SPIRIT_HEALER_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x223*/ { "CMSG_CHARACTER_POINT_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x224*/ { "SMSG_GOSSIP_POI", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x225*/ { "CMSG_CHAT_IGNORED", STATUS_LOGGEDIN, &WorldSession::HandleChatIgnoredOpcode }, + /*0x226*/ { "CMSG_GM_VISION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x227*/ { "CMSG_SERVER_COMMAND", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x228*/ { "CMSG_GM_SILENCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x229*/ { "CMSG_GM_REVEALTO", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22A*/ { "CMSG_GM_RESURRECT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22B*/ { "CMSG_GM_SUMMONMOB", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22C*/ { "CMSG_GM_MOVECORPSE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22D*/ { "CMSG_GM_FREEZE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22E*/ { "CMSG_GM_UBERINVIS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x22F*/ { "CMSG_GM_REQUEST_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x230*/ { "SMSG_GM_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x231*/ { "CMSG_GUILD_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildRankOpcode }, + /*0x232*/ { "CMSG_GUILD_ADD_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildAddRankOpcode }, + /*0x233*/ { "CMSG_GUILD_DEL_RANK", STATUS_LOGGEDIN, &WorldSession::HandleGuildDelRankOpcode }, + /*0x234*/ { "CMSG_GUILD_SET_PUBLIC_NOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildSetPublicNoteOpcode }, + /*0x235*/ { "CMSG_GUILD_SET_OFFICER_NOTE", STATUS_LOGGEDIN, &WorldSession::HandleGuildSetOfficerNoteOpcode }, + /*0x236*/ { "SMSG_LOGIN_VERIFY_WORLD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x237*/ { "CMSG_CLEAR_EXPLORATION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x238*/ { "CMSG_SEND_MAIL", STATUS_LOGGEDIN, &WorldSession::HandleSendMail }, + /*0x239*/ { "SMSG_SEND_MAIL_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x23A*/ { "CMSG_GET_MAIL_LIST", STATUS_LOGGEDIN, &WorldSession::HandleGetMail }, + /*0x23B*/ { "SMSG_MAIL_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x23C*/ { "CMSG_BATTLEFIELD_LIST", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundListOpcode }, + /*0x23D*/ { "SMSG_BATTLEFIELD_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x23E*/ { "CMSG_BATTLEFIELD_JOIN", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x23F*/ { "SMSG_BATTLEFIELD_WIN_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x240*/ { "SMSG_BATTLEFIELD_LOSE_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x241*/ { "CMSG_TAXICLEARNODE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x242*/ { "CMSG_TAXIENABLENODE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x243*/ { "CMSG_ITEM_TEXT_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleItemTextQuery }, + /*0x244*/ { "SMSG_ITEM_TEXT_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x245*/ { "CMSG_MAIL_TAKE_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleTakeMoney }, + /*0x246*/ { "CMSG_MAIL_TAKE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleTakeItem }, + /*0x247*/ { "CMSG_MAIL_MARK_AS_READ", STATUS_LOGGEDIN, &WorldSession::HandleMarkAsRead }, + /*0x248*/ { "CMSG_MAIL_RETURN_TO_SENDER", STATUS_LOGGEDIN, &WorldSession::HandleReturnToSender }, + /*0x249*/ { "CMSG_MAIL_DELETE", STATUS_LOGGEDIN, &WorldSession::HandleMailDelete }, + /*0x24A*/ { "CMSG_MAIL_CREATE_TEXT_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleMailCreateTextItem }, + /*0x24B*/ { "SMSG_SPELLLOGMISS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x24C*/ { "SMSG_SPELLLOGEXECUTE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x24D*/ { "SMSG_DEBUGAURAPROC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x24E*/ { "SMSG_PERIODICAURALOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x24F*/ { "SMSG_SPELLDAMAGESHIELD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x250*/ { "SMSG_SPELLNONMELEEDAMAGELOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x251*/ { "CMSG_LEARN_TALENT", STATUS_LOGGEDIN, &WorldSession::HandleLearnTalentOpcode }, + /*0x252*/ { "SMSG_RESURRECT_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x253*/ { "CMSG_TOGGLE_PVP", STATUS_LOGGEDIN, &WorldSession::HandleTogglePvP }, + /*0x254*/ { "SMSG_ZONE_UNDER_ATTACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x255*/ { "MSG_AUCTION_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleAuctionHelloOpcode }, + /*0x256*/ { "CMSG_AUCTION_SELL_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAuctionSellItem }, + /*0x257*/ { "CMSG_AUCTION_REMOVE_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAuctionRemoveItem }, + /*0x258*/ { "CMSG_AUCTION_LIST_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListItems }, + /*0x259*/ { "CMSG_AUCTION_LIST_OWNER_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListOwnerItems }, + /*0x25A*/ { "CMSG_AUCTION_PLACE_BID", STATUS_LOGGEDIN, &WorldSession::HandleAuctionPlaceBid }, + /*0x25B*/ { "SMSG_AUCTION_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x25C*/ { "SMSG_AUCTION_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x25D*/ { "SMSG_AUCTION_OWNER_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x25E*/ { "SMSG_AUCTION_BIDDER_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x25F*/ { "SMSG_AUCTION_OWNER_NOTIFICATION", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x260*/ { "SMSG_PROCRESIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x261*/ { "SMSG_STANDSTATE_CHANGE_FAILURE_OBSOLETE",STATUS_NEVER,&WorldSession::Handle_ServerSide }, + /*0x262*/ { "SMSG_DISPEL_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x263*/ { "SMSG_SPELLORDAMAGE_IMMUNE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x264*/ { "CMSG_AUCTION_LIST_BIDDER_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleAuctionListBidderItems }, + /*0x265*/ { "SMSG_AUCTION_BIDDER_LIST_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x266*/ { "SMSG_SET_FLAT_SPELL_MODIFIER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x267*/ { "SMSG_SET_PCT_SPELL_MODIFIER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x268*/ { "CMSG_SET_AMMO", STATUS_LOGGEDIN, &WorldSession::HandleSetAmmoOpcode }, + /*0x269*/ { "SMSG_CORPSE_RECLAIM_DELAY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x26A*/ { "CMSG_SET_ACTIVE_MOVER", STATUS_LOGGEDIN, &WorldSession::HandleSetActiveMoverOpcode }, + /*0x26B*/ { "CMSG_PET_CANCEL_AURA", STATUS_LOGGEDIN, &WorldSession::HandlePetCancelAuraOpcode }, + /*0x26C*/ { "CMSG_PLAYER_AI_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x26D*/ { "CMSG_CANCEL_AUTO_REPEAT_SPELL", STATUS_LOGGEDIN, &WorldSession::HandleCancelAutoRepeatSpellOpcode}, + /*0x26E*/ { "MSG_GM_ACCOUNT_ONLINE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x26F*/ { "MSG_LIST_STABLED_PETS", STATUS_LOGGEDIN, &WorldSession::HandleListStabledPetsOpcode }, + /*0x270*/ { "CMSG_STABLE_PET", STATUS_LOGGEDIN, &WorldSession::HandleStablePet }, + /*0x271*/ { "CMSG_UNSTABLE_PET", STATUS_LOGGEDIN, &WorldSession::HandleUnstablePet }, + /*0x272*/ { "CMSG_BUY_STABLE_SLOT", STATUS_LOGGEDIN, &WorldSession::HandleBuyStableSlot }, + /*0x273*/ { "SMSG_STABLE_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x274*/ { "CMSG_STABLE_REVIVE_PET", STATUS_LOGGEDIN, &WorldSession::HandleStableRevivePet }, + /*0x275*/ { "CMSG_STABLE_SWAP_PET", STATUS_LOGGEDIN, &WorldSession::HandleStableSwapPet }, + /*0x276*/ { "MSG_QUEST_PUSH_RESULT", STATUS_LOGGEDIN, &WorldSession::HandleQuestPushResult }, + /*0x277*/ { "SMSG_PLAY_MUSIC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x278*/ { "SMSG_PLAY_OBJECT_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x279*/ { "CMSG_REQUEST_PET_INFO", STATUS_LOGGEDIN, &WorldSession::HandleRequestPetInfoOpcode }, + /*0x27A*/ { "CMSG_FAR_SIGHT", STATUS_LOGGEDIN, &WorldSession::HandleFarSightOpcode }, + /*0x27B*/ { "SMSG_SPELLDISPELLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x27C*/ { "SMSG_DAMAGE_CALC_LOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x27D*/ { "CMSG_ENABLE_DAMAGE_LOG", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x27E*/ { "CMSG_GROUP_CHANGE_SUB_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleGroupChangeSubGroupOpcode }, + /*0x27F*/ { "CMSG_REQUEST_PARTY_MEMBER_STATS", STATUS_LOGGEDIN, &WorldSession::HandleRequestPartyMemberStatsOpcode}, + /*0x280*/ { "CMSG_GROUP_SWAP_SUB_GROUP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x281*/ { "CMSG_RESET_FACTION_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x282*/ { "CMSG_AUTOSTORE_BANK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoStoreBankItemOpcode }, + /*0x283*/ { "CMSG_AUTOBANK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleAutoBankItemOpcode }, + /*0x284*/ { "MSG_QUERY_NEXT_MAIL_TIME", STATUS_LOGGEDIN, &WorldSession::HandleMsgQueryNextMailtime }, + /*0x285*/ { "SMSG_RECEIVED_MAIL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x286*/ { "SMSG_RAID_GROUP_ONLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x287*/ { "CMSG_SET_DURABILITY_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x288*/ { "CMSG_SET_PVP_RANK_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x289*/ { "CMSG_ADD_PVP_MEDAL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x28A*/ { "CMSG_DEL_PVP_MEDAL_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x28B*/ { "CMSG_SET_PVP_TITLE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x28C*/ { "SMSG_PVP_CREDIT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x28D*/ { "SMSG_AUCTION_REMOVED_NOTIFICATION",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x28E*/ { "CMSG_GROUP_RAID_CONVERT", STATUS_LOGGEDIN, &WorldSession::HandleRaidConvertOpcode }, + /*0x28F*/ { "CMSG_GROUP_ASSISTANT_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleGroupAssistantOpcode }, + /*0x290*/ { "CMSG_BUYBACK_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleBuybackItem }, + /*0x291*/ { "SMSG_SERVER_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x292*/ { "CMSG_MEETINGSTONE_JOIN", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x293*/ { "CMSG_MEETINGSTONE_LEAVE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x294*/ { "CMSG_MEETINGSTONE_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x295*/ { "SMSG_MEETINGSTONE_SETQUEUE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x296*/ { "CMSG_MEETINGSTONE_INFO", STATUS_LOGGEDIN, &WorldSession::HandleMeetingStoneInfo }, + /*0x297*/ { "SMSG_MEETINGSTONE_COMPLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x298*/ { "SMSG_MEETINGSTONE_IN_PROGRESS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x299*/ { "SMSG_MEETINGSTONE_MEMBER_ADDED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x29A*/ { "CMSG_GMTICKETSYSTEM_TOGGLE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x29B*/ { "CMSG_CANCEL_GROWTH_AURA", STATUS_LOGGEDIN, &WorldSession::HandleCancelGrowthAuraOpcode }, + /*0x29C*/ { "SMSG_CANCEL_AUTO_REPEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x29D*/ { "SMSG_STANDSTATE_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x29E*/ { "SMSG_LOOT_ALL_PASSED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x29F*/ { "SMSG_LOOT_ROLL_WON", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A0*/ { "CMSG_LOOT_ROLL", STATUS_LOGGEDIN, &WorldSession::HandleLootRoll }, + /*0x2A1*/ { "SMSG_LOOT_START_ROLL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A2*/ { "SMSG_LOOT_ROLL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A3*/ { "CMSG_LOOT_MASTER_GIVE", STATUS_LOGGEDIN, &WorldSession::HandleLootMasterGiveOpcode }, + /*0x2A4*/ { "SMSG_LOOT_MASTER_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A5*/ { "SMSG_SET_FORCED_REACTIONS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A6*/ { "SMSG_SPELL_FAILED_OTHER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A7*/ { "SMSG_GAMEOBJECT_RESET_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2A8*/ { "CMSG_REPAIR_ITEM", STATUS_LOGGEDIN, &WorldSession::HandleRepairItemOpcode }, + /*0x2A9*/ { "SMSG_CHAT_PLAYER_NOT_FOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2AA*/ { "MSG_TALENT_WIPE_CONFIRM", STATUS_LOGGEDIN, &WorldSession::HandleTalentWipeOpcode }, + /*0x2AB*/ { "SMSG_SUMMON_REQUEST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2AC*/ { "CMSG_SUMMON_RESPONSE", STATUS_LOGGEDIN, &WorldSession::HandleSummonResponseOpcode }, + /*0x2AD*/ { "MSG_MOVE_TOGGLE_GRAVITY_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2AE*/ { "SMSG_MONSTER_MOVE_TRANSPORT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2AF*/ { "SMSG_PET_BROKEN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2B0*/ { "MSG_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2B1*/ { "MSG_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2B2*/ { "CMSG_SERVER_BROADCAST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2B3*/ { "CMSG_SELF_RES", STATUS_LOGGEDIN, &WorldSession::HandleSelfResOpcode }, + /*0x2B4*/ { "SMSG_FEIGN_DEATH_RESISTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2B5*/ { "CMSG_RUN_SCRIPT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2B6*/ { "SMSG_SCRIPT_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2B7*/ { "SMSG_DUEL_COUNTDOWN", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2B8*/ { "SMSG_AREA_TRIGGER_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2B9*/ { "CMSG_TOGGLE_HELM", STATUS_LOGGEDIN, &WorldSession::HandleToggleHelmOpcode }, + /*0x2BA*/ { "CMSG_TOGGLE_CLOAK", STATUS_LOGGEDIN, &WorldSession::HandleToggleCloakOpcode }, + /*0x2BB*/ { "SMSG_MEETINGSTONE_JOINFAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2BC*/ { "SMSG_PLAYER_SKINNED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2BD*/ { "SMSG_DURABILITY_DAMAGE_DEATH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2BE*/ { "CMSG_SET_EXPLORATION", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2BF*/ { "CMSG_SET_ACTIONBAR_TOGGLES", STATUS_AUTHED, &WorldSession::HandleSetActionBar }, + /*0x2C0*/ { "UMSG_DELETE_GUILD_CHARTER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2C1*/ { "MSG_PETITION_RENAME", STATUS_LOGGEDIN, &WorldSession::HandlePetitionRenameOpcode }, + /*0x2C2*/ { "SMSG_INIT_WORLD_STATES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2C3*/ { "SMSG_UPDATE_WORLD_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2C4*/ { "CMSG_ITEM_NAME_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleItemNameQueryOpcode }, + /*0x2C5*/ { "SMSG_ITEM_NAME_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2C6*/ { "SMSG_PET_ACTION_FEEDBACK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2C7*/ { "CMSG_CHAR_RENAME", STATUS_AUTHED, &WorldSession::HandleChangePlayerNameOpcode }, + /*0x2C8*/ { "SMSG_CHAR_RENAME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2C9*/ { "CMSG_MOVE_SPLINE_DONE", STATUS_LOGGEDIN, &WorldSession::HandleTaxiNextDestinationOpcode }, + /*0x2CA*/ { "CMSG_MOVE_FALL_RESET", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x2CB*/ { "SMSG_INSTANCE_SAVE_CREATED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2CC*/ { "SMSG_RAID_INSTANCE_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2CD*/ { "CMSG_REQUEST_RAID_INFO", STATUS_LOGGEDIN, &WorldSession::HandleRequestRaidInfoOpcode }, + /*0x2CE*/ { "CMSG_MOVE_TIME_SKIPPED", STATUS_LOGGEDIN, &WorldSession::HandleMoveTimeSkippedOpcode }, + /*0x2CF*/ { "CMSG_MOVE_FEATHER_FALL_ACK", STATUS_LOGGEDIN, &WorldSession::HandleFeatherFallAck }, + /*0x2D0*/ { "CMSG_MOVE_WATER_WALK_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveWaterWalkAck }, + /*0x2D1*/ { "CMSG_MOVE_NOT_ACTIVE_MOVER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2D2*/ { "SMSG_PLAY_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2D3*/ { "CMSG_BATTLEFIELD_STATUS", STATUS_LOGGEDIN, &WorldSession::HandleBattlefieldStatusOpcode }, + /*0x2D4*/ { "SMSG_BATTLEFIELD_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2D5*/ { "CMSG_BATTLEFIELD_PORT", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPlayerPortOpcode}, + /*0x2D6*/ { "MSG_INSPECT_HONOR_STATS", STATUS_LOGGEDIN, &WorldSession::HandleInspectHonorStatsOpcode }, + /*0x2D7*/ { "CMSG_BATTLEMASTER_HELLO", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundHelloOpcode }, + /*0x2D8*/ { "CMSG_MOVE_START_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2D9*/ { "CMSG_MOVE_STOP_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2DA*/ { "SMSG_FORCE_WALK_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2DB*/ { "CMSG_FORCE_WALK_SPEED_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x2DC*/ { "SMSG_FORCE_SWIM_BACK_SPEED_CHANGE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2DD*/ { "CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck }, + /*0x2DE*/ { "SMSG_FORCE_TURN_RATE_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2DF*/ { "CMSG_FORCE_TURN_RATE_CHANGE_ACK", STATUS_LOGGEDIN, &WorldSession::HandleForceSpeedChangeAck }, + /*0x2E0*/ { "MSG_PVP_LOG_DATA", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPVPlogdataOpcode}, + /*0x2E1*/ { "CMSG_LEAVE_BATTLEFIELD", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundLeaveOpcode }, + /*0x2E2*/ { "CMSG_AREA_SPIRIT_HEALER_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleAreaSpiritHealerQueryOpcode}, + /*0x2E3*/ { "CMSG_AREA_SPIRIT_HEALER_QUEUE", STATUS_LOGGEDIN, &WorldSession::HandleAreaSpiritHealerQueueOpcode}, + /*0x2E4*/ { "SMSG_AREA_SPIRIT_HEALER_TIME", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2E5*/ { "CMSG_GM_UNTEACH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2E6*/ { "SMSG_WARDEN_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2E7*/ { "CMSG_WARDEN_DATA", STATUS_LOGGEDIN, &WorldSession::HandleWardenDataOpcode }, + /*0x2E8*/ { "SMSG_GROUP_JOINED_BATTLEGROUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2E9*/ { "MSG_BATTLEGROUND_PLAYER_POSITIONS",STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundPlayerPositionsOpcode}, + /*0x2EA*/ { "CMSG_PET_STOP_ATTACK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2EB*/ { "SMSG_BINDER_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2EC*/ { "SMSG_BATTLEGROUND_PLAYER_JOINED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2ED*/ { "SMSG_BATTLEGROUND_PLAYER_LEFT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2EE*/ { "CMSG_BATTLEMASTER_JOIN", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundJoinOpcode }, + /*0x2EF*/ { "SMSG_ADDON_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F0*/ { "CMSG_PET_UNLEARN", STATUS_LOGGEDIN, &WorldSession::HandlePetUnlearnOpcode }, + /*0x2F1*/ { "SMSG_PET_UNLEARN_CONFIRM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F2*/ { "SMSG_PARTY_MEMBER_STATS_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F3*/ { "CMSG_PET_SPELL_AUTOCAST", STATUS_LOGGEDIN, &WorldSession::HandlePetSpellAutocastOpcode }, + /*0x2F4*/ { "SMSG_WEATHER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F5*/ { "SMSG_PLAY_TIME_WARNING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F6*/ { "SMSG_MINIGAME_SETUP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F7*/ { "SMSG_MINIGAME_STATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2F8*/ { "CMSG_MINIGAME_MOVE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x2F9*/ { "SMSG_MINIGAME_MOVE_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2FA*/ { "SMSG_RAID_INSTANCE_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2FB*/ { "SMSG_COMPRESSED_MOVES", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2FC*/ { "CMSG_GUILD_INFO_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildChangeInfoOpcode }, + /*0x2FD*/ { "SMSG_CHAT_RESTRICTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2FE*/ { "SMSG_SPLINE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x2FF*/ { "SMSG_SPLINE_SET_RUN_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x300*/ { "SMSG_SPLINE_SET_SWIM_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x301*/ { "SMSG_SPLINE_SET_WALK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x302*/ { "SMSG_SPLINE_SET_SWIM_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x303*/ { "SMSG_SPLINE_SET_TURN_RATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x304*/ { "SMSG_SPLINE_MOVE_UNROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x305*/ { "SMSG_SPLINE_MOVE_FEATHER_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x306*/ { "SMSG_SPLINE_MOVE_NORMAL_FALL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x307*/ { "SMSG_SPLINE_MOVE_SET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x308*/ { "SMSG_SPLINE_MOVE_UNSET_HOVER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x309*/ { "SMSG_SPLINE_MOVE_WATER_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30A*/ { "SMSG_SPLINE_MOVE_LAND_WALK", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30B*/ { "SMSG_SPLINE_MOVE_START_SWIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30C*/ { "SMSG_SPLINE_MOVE_STOP_SWIM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30D*/ { "SMSG_SPLINE_MOVE_SET_RUN_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30E*/ { "SMSG_SPLINE_MOVE_SET_WALK_MODE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x30F*/ { "CMSG_GM_NUKE_ACCOUNT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x310*/ { "MSG_GM_DESTROY_CORPSE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x311*/ { "CMSG_GM_DESTROY_ONLINE_CORPSE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x312*/ { "CMSG_ACTIVATETAXIEXPRESS", STATUS_LOGGEDIN, &WorldSession::HandleActivateTaxiFarOpcode }, + /*0x313*/ { "SMSG_SET_FACTION_ATWAR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x314*/ { "SMSG_GAMETIMEBIAS_SET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x315*/ { "CMSG_DEBUG_ACTIONS_START", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x316*/ { "CMSG_DEBUG_ACTIONS_STOP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x317*/ { "CMSG_SET_FACTION_INACTIVE", STATUS_LOGGEDIN, &WorldSession::HandleSetWatchedFactionInactiveOpcode}, + /*0x318*/ { "CMSG_SET_WATCHED_FACTION", STATUS_LOGGEDIN, &WorldSession::HandleSetWatchedFactionIndexOpcode}, + /*0x319*/ { "MSG_MOVE_TIME_SKIPPED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x31A*/ { "SMSG_SPLINE_MOVE_ROOT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x31B*/ { "CMSG_SET_EXPLORATION_ALL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x31C*/ { "SMSG_INVALIDATE_PLAYER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x31D*/ { "CMSG_RESET_INSTANCES", STATUS_LOGGEDIN, &WorldSession::HandleResetInstancesOpcode }, + /*0x31E*/ { "SMSG_INSTANCE_RESET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x31F*/ { "SMSG_INSTANCE_RESET_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x320*/ { "SMSG_UPDATE_LAST_INSTANCE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x321*/ { "MSG_RAID_TARGET_UPDATE", STATUS_LOGGEDIN, &WorldSession::HandleRaidIconTargetOpcode }, + /*0x322*/ { "MSG_RAID_READY_CHECK", STATUS_LOGGEDIN, &WorldSession::HandleRaidReadyCheckOpcode }, + /*0x323*/ { "CMSG_LUA_USAGE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x324*/ { "SMSG_PET_ACTION_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x325*/ { "SMSG_PET_DISMISS_SOUND", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x326*/ { "SMSG_GHOSTEE_GONE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x327*/ { "CMSG_GM_UPDATE_TICKET_STATUS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x328*/ { "SMSG_GM_TICKET_STATUS_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x329*/ { "MSG_SET_DUNGEON_DIFFICULTY", STATUS_LOGGEDIN, &WorldSession::HandleDungeonDifficultyOpcode }, + /*0x32A*/ { "CMSG_GMSURVEY_SUBMIT", STATUS_LOGGEDIN, &WorldSession::HandleGMSurveySubmit }, + /*0x32B*/ { "SMSG_UPDATE_INSTANCE_OWNERSHIP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x32C*/ { "CMSG_IGNORE_KNOCKBACK_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x32D*/ { "SMSG_CHAT_PLAYER_AMBIGUOUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x32E*/ { "MSG_DELAY_GHOST_TELEPORT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x32F*/ { "SMSG_SPELLINSTAKILLLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x330*/ { "SMSG_SPELL_UPDATE_CHAIN_TARGETS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x331*/ { "CMSG_CHAT_FILTERED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x332*/ { "SMSG_EXPECTED_SPAM_RECORDS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x333*/ { "SMSG_SPELLSTEALLOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x334*/ { "CMSG_LOTTERY_QUERY_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x335*/ { "SMSG_LOTTERY_QUERY_RESULT_OBSOLETE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x336*/ { "CMSG_BUY_LOTTERY_TICKET_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x337*/ { "SMSG_LOTTERY_RESULT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x338*/ { "SMSG_CHARACTER_PROFILE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x339*/ { "SMSG_CHARACTER_PROFILE_REALM_CONNECTED",STATUS_NEVER,&WorldSession::Handle_ServerSide }, + /*0x33A*/ { "SMSG_DEFENSE_MESSAGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x33B*/ { "SMSG_INSTANCE_DIFFICULTY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x33C*/ { "MSG_GM_RESETINSTANCELIMIT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x33D*/ { "SMSG_MOTD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x33E*/ { "SMSG_MOVE_SET_FLIGHT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x33F*/ { "SMSG_MOVE_UNSET_FLIGHT_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x340*/ { "CMSG_MOVE_FLIGHT_ACK_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x341*/ { "MSG_MOVE_START_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x342*/ { "MSG_MOVE_STOP_SWIM_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x343*/ { "SMSG_MOVE_SET_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x344*/ { "SMSG_MOVE_UNSET_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x345*/ { "CMSG_MOVE_SET_CAN_FLY_ACK", STATUS_LOGGEDIN, &WorldSession::HandleMoveFlyModeChangeAckOpcode}, + /*0x346*/ { "CMSG_MOVE_SET_FLY", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x347*/ { "CMSG_SOCKET_GEMS", STATUS_LOGGEDIN, &WorldSession::HandleSocketOpcode }, + /*0x348*/ { "CMSG_ARENA_TEAM_CREATE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x349*/ { "SMSG_ARENA_TEAM_COMMAND_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x34A*/ { "UMSG_UPDATE_ARENA_TEAM_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x34B*/ { "CMSG_ARENA_TEAM_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamQueryOpcode }, + /*0x34C*/ { "SMSG_ARENA_TEAM_QUERY_RESPONSE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x34D*/ { "CMSG_ARENA_TEAM_ROSTER", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamRosterOpcode }, + /*0x34E*/ { "SMSG_ARENA_TEAM_ROSTER", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x34F*/ { "CMSG_ARENA_TEAM_INVITE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamAddMemberOpcode }, + /*0x350*/ { "SMSG_ARENA_TEAM_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x351*/ { "CMSG_ARENA_TEAM_ACCEPT", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamInviteAcceptOpcode}, + /*0x352*/ { "CMSG_ARENA_TEAM_DECLINE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamInviteDeclineOpcode}, + /*0x353*/ { "CMSG_ARENA_TEAM_LEAVE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamLeaveOpcode }, + /*0x354*/ { "CMSG_ARENA_TEAM_REMOVE", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamRemoveFromTeamOpcode}, + /*0x355*/ { "CMSG_ARENA_TEAM_DISBAND", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamDisbandOpcode }, + /*0x356*/ { "CMSG_ARENA_TEAM_LEADER", STATUS_LOGGEDIN, &WorldSession::HandleArenaTeamPromoteToCaptainOpcode}, + /*0x357*/ { "SMSG_ARENA_TEAM_EVENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x358*/ { "CMSG_BATTLEMASTER_JOIN_ARENA", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundArenaJoin }, + /*0x359*/ { "MSG_MOVE_START_ASCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x35A*/ { "MSG_MOVE_STOP_ASCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x35B*/ { "SMSG_ARENA_TEAM_STATS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x35C*/ { "CMSG_LFG_SET_AUTOJOIN", STATUS_AUTHED, &WorldSession::HandleLfgAutoJoinOpcode }, + /*0x35D*/ { "CMSG_LFG_CLEAR_AUTOJOIN", STATUS_LOGGEDIN, &WorldSession::HandleLfgCancelAutoJoinOpcode }, + /*0x35E*/ { "CMSG_LFM_SET_AUTOFILL", STATUS_AUTHED, &WorldSession::HandleLfmAutoAddMembersOpcode }, + /*0x35F*/ { "CMSG_LFM_CLEAR_AUTOFILL", STATUS_LOGGEDIN, &WorldSession::HandleLfmCancelAutoAddmembersOpcode}, + /*0x360*/ { "CMSG_ACCEPT_LFG_MATCH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x361*/ { "CMSG_DECLINE_LFG_MATCH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x362*/ { "CMSG_CANCEL_PENDING_LFG", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x363*/ { "CMSG_CLEAR_LOOKING_FOR_GROUP", STATUS_LOGGEDIN, &WorldSession::HandleLfgClearOpcode }, + /*0x364*/ { "CMSG_CLEAR_LOOKING_FOR_MORE", STATUS_LOGGEDIN, &WorldSession::HandleLfmSetNoneOpcode }, + /*0x365*/ { "CMSG_SET_LOOKING_FOR_MORE", STATUS_LOGGEDIN, &WorldSession::HandleLfmSetOpcode }, + /*0x366*/ { "CMSG_SET_LFG_COMMENT", STATUS_LOGGEDIN, &WorldSession::HandleLfgSetCommentOpcode }, + /*0x367*/ { "SMSG_LFG_TIMEDOUT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x368*/ { "SMSG_LFG_OTHER_TIMEDOUT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x369*/ { "SMSG_LFG_AUTOJOIN_FAILED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36A*/ { "SMSG_LFG_AUTOJOIN_FAILED_NO_PLAYER",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36B*/ { "SMSG_LFG_LEADER_IS_LFM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36C*/ { "SMSG_LFG_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36D*/ { "SMSG_LFG_UPDATE_LFM", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36E*/ { "SMSG_LFG_UPDATE_LFG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x36F*/ { "SMSG_LFG_UPDATE_QUEUED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x370*/ { "SMSG_LFG_PENDING_INVITE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x371*/ { "SMSG_LFG_PENDING_MATCH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x372*/ { "SMSG_LFG_PENDING_MATCH_DONE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x373*/ { "SMSG_TITLE_EARNED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x374*/ { "CMSG_SET_TITLE", STATUS_LOGGEDIN, &WorldSession::HandleChooseTitleOpcode }, + /*0x375*/ { "CMSG_CANCEL_MOUNT_AURA", STATUS_LOGGEDIN, &WorldSession::HandleDismountOpcode }, + /*0x376*/ { "SMSG_ARENA_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x377*/ { "MSG_INSPECT_ARENA_TEAMS", STATUS_LOGGEDIN, &WorldSession::HandleInspectArenaStatsOpcode }, + /*0x378*/ { "SMSG_DEATH_RELEASE_LOC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x379*/ { "CMSG_CANCEL_TEMP_ENCHANTMENT", STATUS_LOGGEDIN, &WorldSession::HandleCancelTempItemEnchantmentOpcode}, + /*0x37A*/ { "SMSG_FORCED_DEATH_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x37B*/ { "CMSG_CHEAT_SET_HONOR_CURRENCY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x37C*/ { "CMSG_CHEAT_SET_ARENA_CURRENCY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x37D*/ { "MSG_MOVE_SET_FLIGHT_SPEED_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x37E*/ { "MSG_MOVE_SET_FLIGHT_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x37F*/ { "MSG_MOVE_SET_FLIGHT_BACK_SPEED_CHEAT",STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x380*/ { "MSG_MOVE_SET_FLIGHT_BACK_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x381*/ { "SMSG_FORCE_FLIGHT_SPEED_CHANGE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x382*/ { "CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck }, + /*0x383*/ { "SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x384*/ { "CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK",STATUS_LOGGEDIN,&WorldSession::HandleForceSpeedChangeAck }, + /*0x385*/ { "SMSG_SPLINE_SET_FLIGHT_SPEED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x386*/ { "SMSG_SPLINE_SET_FLIGHT_BACK_SPEED",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x387*/ { "CMSG_MAELSTROM_INVALIDATE_CACHE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x388*/ { "SMSG_FLIGHT_SPLINE_SYNC", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x389*/ { "CMSG_SET_TAXI_BENCHMARK_MODE", STATUS_AUTHED, &WorldSession::HandleSetTaxiBenchmarkOpcode }, + /*0x38A*/ { "SMSG_JOINED_BATTLEGROUND_QUEUE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x38B*/ { "SMSG_REALM_SPLIT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x38C*/ { "CMSG_REALM_SPLIT", STATUS_AUTHED, &WorldSession::HandleRealmStateRequestOpcode }, + /*0x38D*/ { "CMSG_MOVE_CHNG_TRANSPORT", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x38E*/ { "MSG_PARTY_ASSIGNMENT", STATUS_LOGGEDIN, &WorldSession::HandleGroupPromoteOpcode }, + /*0x38F*/ { "SMSG_OFFER_PETITION_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x390*/ { "SMSG_TIME_SYNC_REQ", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x391*/ { "CMSG_TIME_SYNC_RESP", STATUS_LOGGEDIN, &WorldSession::HandleAllowMoveAckOpcode }, + /*0x392*/ { "CMSG_SEND_LOCAL_EVENT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x393*/ { "CMSG_SEND_GENERAL_TRIGGER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x394*/ { "CMSG_SEND_COMBAT_TRIGGER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x395*/ { "CMSG_MAELSTROM_GM_SENT_MAIL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x396*/ { "SMSG_RESET_FAILED_NOTIFY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x397*/ { "SMSG_REAL_GROUP_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x398*/ { "SMSG_LFG_DISABLED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x399*/ { "CMSG_ACTIVE_PVP_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x39A*/ { "CMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x39B*/ { "SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE",STATUS_NEVER,&WorldSession::Handle_ServerSide }, + /*0x39C*/ { "SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE_WRITE_FILE",STATUS_NEVER,&WorldSession::Handle_ServerSide}, + /*0x39D*/ { "SMSG_UPDATE_COMBO_POINTS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x39E*/ { "SMSG_VOICE_SESSION_ROSTER_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x39F*/ { "SMSG_VOICE_SESSION_LEAVE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A0*/ { "SMSG_VOICE_SESSION_ADJUST_PRIORITY",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A1*/ { "CMSG_VOICE_SET_TALKER_MUTED_REQUEST",STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3A2*/ { "SMSG_VOICE_SET_TALKER_MUTED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A3*/ { "SMSG_INIT_EXTRA_AURA_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A4*/ { "SMSG_SET_EXTRA_AURA_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A5*/ { "SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A6*/ { "SMSG_CLEAR_EXTRA_AURA_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3A7*/ { "MSG_MOVE_START_DESCEND", STATUS_LOGGEDIN, &WorldSession::HandleMovementOpcodes }, + /*0x3A8*/ { "CMSG_IGNORE_REQUIREMENTS_CHEAT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3A9*/ { "SMSG_IGNORE_REQUIREMENTS_CHEAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3AA*/ { "SMSG_SPELL_CHANCE_PROC_LOG", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3AB*/ { "CMSG_MOVE_SET_RUN_SPEED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3AC*/ { "SMSG_DISMOUNT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3AD*/ { "MSG_MOVE_UPDATE_CAN_FLY", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3AE*/ { "MSG_RAID_READY_CHECK_CONFIRM", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3AF*/ { "CMSG_VOICE_SESSION_ENABLE", STATUS_AUTHED, &WorldSession::HandleVoiceSettingsOpcode }, + /*0x3B0*/ { "SMSG_VOICE_PARENTAL_CONTROLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3B1*/ { "CMSG_GM_WHISPER", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3B2*/ { "SMSG_GM_MESSAGECHAT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3B3*/ { "MSG_GM_GEARRATING", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3B4*/ { "CMSG_COMMENTATOR_ENABLE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3B5*/ { "SMSG_COMMENTATOR_STATE_CHANGED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3B6*/ { "CMSG_COMMENTATOR_GET_MAP_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3B7*/ { "SMSG_COMMENTATOR_MAP_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3B8*/ { "CMSG_COMMENTATOR_GET_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3B9*/ { "SMSG_COMMENTATOR_GET_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3BA*/ { "SMSG_COMMENTATOR_PLAYER_INFO", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3BB*/ { "CMSG_COMMENTATOR_ENTER_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3BC*/ { "CMSG_COMMENTATOR_EXIT_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3BD*/ { "CMSG_COMMENTATOR_INSTANCE_COMMAND",STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3BE*/ { "SMSG_CLEAR_TARGET", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3BF*/ { "CMSG_BOT_DETECTED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3C0*/ { "SMSG_CROSSED_INEBRIATION_THRESHOLD",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3C1*/ { "CMSG_CHEAT_PLAYER_LOGIN", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3C2*/ { "CMSG_CHEAT_PLAYER_LOOKUP", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3C3*/ { "SMSG_CHEAT_PLAYER_LOOKUP", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3C4*/ { "SMSG_KICK_REASON", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3C5*/ { "MSG_RAID_READY_CHECK_FINISHED", STATUS_LOGGEDIN, &WorldSession::HandleRaidReadyCheckFinishOpcode}, + /*0x3C6*/ { "CMSG_COMPLAIN", STATUS_LOGGEDIN, &WorldSession::HandleReportSpamOpcode }, + /*0x3C7*/ { "SMSG_COMPLAIN_RESULT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3C8*/ { "SMSG_FEATURE_SYSTEM_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3C9*/ { "CMSG_GM_SHOW_COMPLAINTS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CA*/ { "CMSG_GM_UNSQUELCH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CB*/ { "CMSG_CHANNEL_SILENCE_VOICE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CC*/ { "CMSG_CHANNEL_SILENCE_ALL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CD*/ { "CMSG_CHANNEL_UNSILENCE_VOICE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CE*/ { "CMSG_CHANNEL_UNSILENCE_ALL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3CF*/ { "CMSG_TARGET_CAST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3D0*/ { "CMSG_TARGET_SCRIPT_CAST", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3D1*/ { "CMSG_CHANNEL_DISPLAY_LIST", STATUS_LOGGEDIN, &WorldSession::HandleChannelRosterQuery }, + /*0x3D2*/ { "CMSG_SET_ACTIVE_VOICE_CHANNEL", STATUS_AUTHED, &WorldSession::HandleChannelVoiceChatQuery }, + /*0x3D3*/ { "CMSG_GET_CHANNEL_MEMBER_COUNT", STATUS_LOGGEDIN, &WorldSession::HandleChannelInfoQuery }, + /*0x3D4*/ { "SMSG_CHANNEL_MEMBER_COUNT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3D5*/ { "CMSG_CHANNEL_VOICE_ON", STATUS_LOGGEDIN, &WorldSession::HandleChannelEnableVoiceOpcode }, + /*0x3D6*/ { "CMSG_CHANNEL_VOICE_OFF", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3D7*/ { "CMSG_DEBUG_LIST_TARGETS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3D8*/ { "SMSG_DEBUG_LIST_TARGETS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3D9*/ { "SMSG_AVAILABLE_VOICE_CHANNEL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3DA*/ { "CMSG_ADD_VOICE_IGNORE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3DB*/ { "CMSG_DEL_VOICE_IGNORE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3DC*/ { "CMSG_PARTY_SILENCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3DD*/ { "CMSG_PARTY_UNSILENCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3DE*/ { "MSG_NOTIFY_PARTY_SQUELCH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3DF*/ { "SMSG_COMSAT_RECONNECT_TRY", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3E0*/ { "SMSG_COMSAT_DISCONNECT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3E1*/ { "SMSG_COMSAT_CONNECT_FAIL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3E2*/ { "SMSG_VOICE_CHAT_STATUS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3E3*/ { "CMSG_REPORT_PVP_AFK", STATUS_LOGGEDIN, &WorldSession::HandleBattleGroundReportAFK }, + /*0x3E4*/ { "CMSG_REPORT_PVP_AFK_RESULT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3E5*/ { "CMSG_GUILD_BANKER_ACTIVATE", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankQuery }, + /*0x3E6*/ { "CMSG_GUILD_BANK_QUERY_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankTabColon }, + /*0x3E7*/ { "SMSG_GUILD_BANK_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3E8*/ { "CMSG_GUILD_BANK_SWAP_ITEMS", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankDepositItem }, + /*0x3E9*/ { "CMSG_GUILD_BANK_BUY_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankBuyTab }, + /*0x3EA*/ { "CMSG_GUILD_BANK_UPDATE_TAB", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankModifyTab }, + /*0x3EB*/ { "CMSG_GUILD_BANK_DEPOSIT_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankDeposit }, + /*0x3EC*/ { "CMSG_GUILD_BANK_WITHDRAW_MONEY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankWithdraw }, + /*0x3ED*/ { "MSG_GUILD_BANK_LOG_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankLog }, + /*0x3EE*/ { "CMSG_SET_CHANNEL_WATCH", STATUS_LOGGEDIN, &WorldSession::HandleChannelJoinNotify }, + /*0x3EF*/ { "SMSG_USERLIST_ADD", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F0*/ { "SMSG_USERLIST_REMOVE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F1*/ { "SMSG_USERLIST_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F2*/ { "CMSG_CLEAR_CHANNEL_WATCH", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3F3*/ { "SMSG_INSPECT_TALENT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F4*/ { "SMSG_GOGOGO_OBSOLETE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F5*/ { "SMSG_ECHO_PARTY_SQUELCH", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F6*/ { "CMSG_SET_TITLE_SUFFIX", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3F7*/ { "CMSG_SPELLCLICK", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3F8*/ { "SMSG_LOOT_LIST", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3F9*/ { "CMSG_GM_CHARACTER_RESTORE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3FA*/ { "CMSG_GM_CHARACTER_SAVE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x3FB*/ { "SMSG_VOICESESSION_FULL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x3FC*/ { "MSG_GUILD_PERMISSIONS", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankGetRights }, + /*0x3FD*/ { "MSG_GUILD_BANK_MONEY_WITHDRAWN", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankGetMoneyAmount }, + /*0x3FE*/ { "MSG_GUILD_EVENT_LOG_QUERY", STATUS_LOGGEDIN, &WorldSession::HandleGuildEventLogOpcode }, + /*0x3FF*/ { "CMSG_MAELSTROM_RENAME_GUILD", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x400*/ { "CMSG_GET_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x401*/ { "SMSG_MIRRORIMAGE_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x402*/ { "SMSG_FORCE_DISPLAY_UPDATE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x403*/ { "SMSG_SPELL_CHANCE_RESIST_PUSHBACK",STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x404*/ { "CMSG_IGNORE_DIMINISHING_RETURNS_CHEAT",STATUS_NEVER,&WorldSession::Handle_NULL }, + /*0x405*/ { "SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT",STATUS_NEVER,&WorldSession::Handle_ServerSide }, + /*0x406*/ { "CMSG_KEEP_ALIVE", STATUS_NEVER, &WorldSession::Handle_EarlyProccess }, + /*0x407*/ { "SMSG_RAID_READY_CHECK_ERROR", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x408*/ { "CMSG_OPT_OUT_OF_LOOT", STATUS_AUTHED, &WorldSession::HandleGroupPassOnLootOpcode }, + /*0x409*/ { "MSG_QUERY_GUILD_BANK_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankTabText }, + /*0x40A*/ { "CMSG_SET_GUILD_BANK_TEXT", STATUS_LOGGEDIN, &WorldSession::HandleGuildBankSetTabText }, + /*0x40B*/ { "CMSG_SET_GRANTABLE_LEVELS", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x40C*/ { "CMSG_GRANT_LEVEL", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x40D*/ { "CMSG_REFER_A_FRIEND", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x40E*/ { "MSG_GM_CHANGE_ARENA_RATING", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x40F*/ { "CMSG_DECLINE_CHANNEL_INVITE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x410*/ { "CMSG_GROUPACTION_THROTTLED", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x411*/ { "SMSG_OVERRIDE_LIGHT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x412*/ { "SMSG_TOTEM_CREATED", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x413*/ { "CMSG_TOTEM_DESTROYED", STATUS_LOGGEDIN, &WorldSession::HandleTotemDestroy }, + /*0x414*/ { "CMSG_EXPIRE_RAID_INSTANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x415*/ { "CMSG_NO_SPELL_VARIANCE", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x416*/ { "CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY",STATUS_LOGGEDIN,&WorldSession::HandleQuestgiverStatusQueryMultipleOpcode}, + /*0x417*/ { "SMSG_QUESTGIVER_STATUS_MULTIPLE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x418*/ { "CMSG_SET_PLAYER_DECLINED_NAMES", STATUS_AUTHED, &WorldSession::HandleDeclinedPlayerNameOpcode }, + /*0x419*/ { "SMSG_SET_PLAYER_DECLINED_NAMES_RESULT",STATUS_NEVER,&WorldSession::Handle_ServerSide }, + /*0x41A*/ { "CMSG_QUERY_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x41B*/ { "CMSG_CLEAR_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x41C*/ { "SMSG_SERVER_BUCK_DATA", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x41D*/ { "SMSG_SEND_UNLEARN_SPELLS", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x41E*/ { "SMSG_PROPOSE_LEVEL_GRANT", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x41F*/ { "CMSG_ACCEPT_LEVEL_GRANT", STATUS_NEVER, &WorldSession::Handle_NULL }, + /*0x420*/ { "SMSG_REFER_A_FRIEND_FAILURE", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x421*/ { "SMSG_SPLINE_MOVE_SET_FLYING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x422*/ { "SMSG_SPLINE_MOVE_UNSET_FLYING", STATUS_NEVER, &WorldSession::Handle_ServerSide }, + /*0x423*/ { "SMSG_SUMMON_CANCEL", STATUS_NEVER, &WorldSession::Handle_ServerSide }, +}; diff --git a/src/game/Opcodes.h b/src/game/Opcodes.h new file mode 100644 index 00000000000..423fad2351f --- /dev/null +++ b/src/game/Opcodes.h @@ -0,0 +1,1125 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup u2w +/// @{ +/// \file + +#ifndef _OPCODES_H +#define _OPCODES_H + +#include "Common.h" + +/// List of Opcodes +enum Opcodes +{ + MSG_NULL_ACTION = 0x000, + CMSG_BOOTME = 0x001, + CMSG_DBLOOKUP = 0x002, + SMSG_DBLOOKUP = 0x003, + CMSG_QUERY_OBJECT_POSITION = 0x004, + SMSG_QUERY_OBJECT_POSITION = 0x005, + CMSG_QUERY_OBJECT_ROTATION = 0x006, + SMSG_QUERY_OBJECT_ROTATION = 0x007, + CMSG_WORLD_TELEPORT = 0x008, + CMSG_TELEPORT_TO_UNIT = 0x009, + CMSG_ZONE_MAP = 0x00A, + SMSG_ZONE_MAP = 0x00B, + CMSG_DEBUG_CHANGECELLZONE = 0x00C, + CMSG_EMBLAZON_TABARD_OBSOLETE = 0x00D, + CMSG_UNEMBLAZON_TABARD_OBSOLETE = 0x00E, + CMSG_RECHARGE = 0x00F, + CMSG_LEARN_SPELL = 0x010, + CMSG_CREATEMONSTER = 0x011, + CMSG_DESTROYMONSTER = 0x012, + CMSG_CREATEITEM = 0x013, + CMSG_CREATEGAMEOBJECT = 0x014, + SMSG_CHECK_FOR_BOTS = 0x015, + CMSG_MAKEMONSTERATTACKGUID = 0x016, + CMSG_BOT_DETECTED2 = 0x017, + CMSG_FORCEACTION = 0x018, + CMSG_FORCEACTIONONOTHER = 0x019, + CMSG_FORCEACTIONSHOW = 0x01A, + SMSG_FORCEACTIONSHOW = 0x01B, + CMSG_PETGODMODE = 0x01C, + SMSG_PETGODMODE = 0x01D, + SMSG_DEBUGINFOSPELLMISS_OBSOLETE = 0x01E, + CMSG_WEATHER_SPEED_CHEAT = 0x01F, + CMSG_UNDRESSPLAYER = 0x020, + CMSG_BEASTMASTER = 0x021, + CMSG_GODMODE = 0x022, + SMSG_GODMODE = 0x023, + CMSG_CHEAT_SETMONEY = 0x024, + CMSG_LEVEL_CHEAT = 0x025, + CMSG_PET_LEVEL_CHEAT = 0x026, + CMSG_SET_WORLDSTATE = 0x027, + CMSG_COOLDOWN_CHEAT = 0x028, + CMSG_USE_SKILL_CHEAT = 0x029, + CMSG_FLAG_QUEST = 0x02A, + CMSG_FLAG_QUEST_FINISH = 0x02B, + CMSG_CLEAR_QUEST = 0x02C, + CMSG_SEND_EVENT = 0x02D, + CMSG_DEBUG_AISTATE = 0x02E, + SMSG_DEBUG_AISTATE = 0x02F, + CMSG_DISABLE_PVP_CHEAT = 0x030, + CMSG_ADVANCE_SPAWN_TIME = 0x031, + CMSG_PVP_PORT_OBSOLETE = 0x032, + CMSG_AUTH_SRP6_BEGIN = 0x033, + CMSG_AUTH_SRP6_PROOF = 0x034, + CMSG_AUTH_SRP6_RECODE = 0x035, + CMSG_CHAR_CREATE = 0x036, + CMSG_CHAR_ENUM = 0x037, + CMSG_CHAR_DELETE = 0x038, + SMSG_AUTH_SRP6_RESPONSE = 0x039, + SMSG_CHAR_CREATE = 0x03A, + SMSG_CHAR_ENUM = 0x03B, + SMSG_CHAR_DELETE = 0x03C, + CMSG_PLAYER_LOGIN = 0x03D, + SMSG_NEW_WORLD = 0x03E, + SMSG_TRANSFER_PENDING = 0x03F, + SMSG_TRANSFER_ABORTED = 0x040, + SMSG_CHARACTER_LOGIN_FAILED = 0x041, + SMSG_LOGIN_SETTIMESPEED = 0x042, + SMSG_GAMETIME_UPDATE = 0x043, + CMSG_GAMETIME_SET = 0x044, + SMSG_GAMETIME_SET = 0x045, + CMSG_GAMESPEED_SET = 0x046, + SMSG_GAMESPEED_SET = 0x047, + CMSG_SERVERTIME = 0x048, + SMSG_SERVERTIME = 0x049, + CMSG_PLAYER_LOGOUT = 0x04A, + CMSG_LOGOUT_REQUEST = 0x04B, + SMSG_LOGOUT_RESPONSE = 0x04C, + SMSG_LOGOUT_COMPLETE = 0x04D, + CMSG_LOGOUT_CANCEL = 0x04E, + SMSG_LOGOUT_CANCEL_ACK = 0x04F, + CMSG_NAME_QUERY = 0x050, + SMSG_NAME_QUERY_RESPONSE = 0x051, + CMSG_PET_NAME_QUERY = 0x052, + SMSG_PET_NAME_QUERY_RESPONSE = 0x053, + CMSG_GUILD_QUERY = 0x054, + SMSG_GUILD_QUERY_RESPONSE = 0x055, + CMSG_ITEM_QUERY_SINGLE = 0x056, + CMSG_ITEM_QUERY_MULTIPLE = 0x057, + SMSG_ITEM_QUERY_SINGLE_RESPONSE = 0x058, + SMSG_ITEM_QUERY_MULTIPLE_RESPONSE = 0x059, + CMSG_PAGE_TEXT_QUERY = 0x05A, + SMSG_PAGE_TEXT_QUERY_RESPONSE = 0x05B, + CMSG_QUEST_QUERY = 0x05C, + SMSG_QUEST_QUERY_RESPONSE = 0x05D, + CMSG_GAMEOBJECT_QUERY = 0x05E, + SMSG_GAMEOBJECT_QUERY_RESPONSE = 0x05F, + CMSG_CREATURE_QUERY = 0x060, + SMSG_CREATURE_QUERY_RESPONSE = 0x061, + CMSG_WHO = 0x062, + SMSG_WHO = 0x063, + CMSG_WHOIS = 0x064, + SMSG_WHOIS = 0x065, + CMSG_CONTACT_LIST = 0x066, + SMSG_CONTACT_LIST = 0x067, + SMSG_FRIEND_STATUS = 0x068, + CMSG_ADD_FRIEND = 0x069, + CMSG_DEL_FRIEND = 0x06A, + CMSG_SET_CONTACT_NOTES = 0x06B, + CMSG_ADD_IGNORE = 0x06C, + CMSG_DEL_IGNORE = 0x06D, + CMSG_GROUP_INVITE = 0x06E, + SMSG_GROUP_INVITE = 0x06F, + CMSG_GROUP_CANCEL = 0x070, + SMSG_GROUP_CANCEL = 0x071, + CMSG_GROUP_ACCEPT = 0x072, + CMSG_GROUP_DECLINE = 0x073, + SMSG_GROUP_DECLINE = 0x074, + CMSG_GROUP_UNINVITE = 0x075, + CMSG_GROUP_UNINVITE_GUID = 0x076, + SMSG_GROUP_UNINVITE = 0x077, + CMSG_GROUP_SET_LEADER = 0x078, + SMSG_GROUP_SET_LEADER = 0x079, + CMSG_LOOT_METHOD = 0x07A, + CMSG_GROUP_DISBAND = 0x07B, + SMSG_GROUP_DESTROYED = 0x07C, + SMSG_GROUP_LIST = 0x07D, + SMSG_PARTY_MEMBER_STATS = 0x07E, + SMSG_PARTY_COMMAND_RESULT = 0x07F, + UMSG_UPDATE_GROUP_MEMBERS = 0x080, + CMSG_GUILD_CREATE = 0x081, + CMSG_GUILD_INVITE = 0x082, + SMSG_GUILD_INVITE = 0x083, + CMSG_GUILD_ACCEPT = 0x084, + CMSG_GUILD_DECLINE = 0x085, + SMSG_GUILD_DECLINE = 0x086, + CMSG_GUILD_INFO = 0x087, + SMSG_GUILD_INFO = 0x088, + CMSG_GUILD_ROSTER = 0x089, + SMSG_GUILD_ROSTER = 0x08A, + CMSG_GUILD_PROMOTE = 0x08B, + CMSG_GUILD_DEMOTE = 0x08C, + CMSG_GUILD_LEAVE = 0x08D, + CMSG_GUILD_REMOVE = 0x08E, + CMSG_GUILD_DISBAND = 0x08F, + CMSG_GUILD_LEADER = 0x090, + CMSG_GUILD_MOTD = 0x091, + SMSG_GUILD_EVENT = 0x092, + SMSG_GUILD_COMMAND_RESULT = 0x093, + UMSG_UPDATE_GUILD = 0x094, + CMSG_MESSAGECHAT = 0x095, + SMSG_MESSAGECHAT = 0x096, + CMSG_JOIN_CHANNEL = 0x097, + CMSG_LEAVE_CHANNEL = 0x098, + SMSG_CHANNEL_NOTIFY = 0x099, + CMSG_CHANNEL_LIST = 0x09A, + SMSG_CHANNEL_LIST = 0x09B, + CMSG_CHANNEL_PASSWORD = 0x09C, + CMSG_CHANNEL_SET_OWNER = 0x09D, + CMSG_CHANNEL_OWNER = 0x09E, + CMSG_CHANNEL_MODERATOR = 0x09F, + CMSG_CHANNEL_UNMODERATOR = 0x0A0, + CMSG_CHANNEL_MUTE = 0x0A1, + CMSG_CHANNEL_UNMUTE = 0x0A2, + CMSG_CHANNEL_INVITE = 0x0A3, + CMSG_CHANNEL_KICK = 0x0A4, + CMSG_CHANNEL_BAN = 0x0A5, + CMSG_CHANNEL_UNBAN = 0x0A6, + CMSG_CHANNEL_ANNOUNCEMENTS = 0x0A7, + CMSG_CHANNEL_MODERATE = 0x0A8, + SMSG_UPDATE_OBJECT = 0x0A9, + SMSG_DESTROY_OBJECT = 0x0AA, + CMSG_USE_ITEM = 0x0AB, + CMSG_OPEN_ITEM = 0x0AC, + CMSG_READ_ITEM = 0x0AD, + SMSG_READ_ITEM_OK = 0x0AE, + SMSG_READ_ITEM_FAILED = 0x0AF, + SMSG_ITEM_COOLDOWN = 0x0B0, + CMSG_GAMEOBJ_USE = 0x0B1, + CMSG_GAMEOBJ_CHAIR_USE_OBSOLETE = 0x0B2, + SMSG_GAMEOBJECT_CUSTOM_ANIM = 0x0B3, + CMSG_AREATRIGGER = 0x0B4, + MSG_MOVE_START_FORWARD = 0x0B5, + MSG_MOVE_START_BACKWARD = 0x0B6, + MSG_MOVE_STOP = 0x0B7, + MSG_MOVE_START_STRAFE_LEFT = 0x0B8, + MSG_MOVE_START_STRAFE_RIGHT = 0x0B9, + MSG_MOVE_STOP_STRAFE = 0x0BA, + MSG_MOVE_JUMP = 0x0BB, + MSG_MOVE_START_TURN_LEFT = 0x0BC, + MSG_MOVE_START_TURN_RIGHT = 0x0BD, + MSG_MOVE_STOP_TURN = 0x0BE, + MSG_MOVE_START_PITCH_UP = 0x0BF, + MSG_MOVE_START_PITCH_DOWN = 0x0C0, + MSG_MOVE_STOP_PITCH = 0x0C1, + MSG_MOVE_SET_RUN_MODE = 0x0C2, + MSG_MOVE_SET_WALK_MODE = 0x0C3, + MSG_MOVE_TOGGLE_LOGGING = 0x0C4, + MSG_MOVE_TELEPORT = 0x0C5, + MSG_MOVE_TELEPORT_CHEAT = 0x0C6, + MSG_MOVE_TELEPORT_ACK = 0x0C7, + MSG_MOVE_TOGGLE_FALL_LOGGING = 0x0C8, + MSG_MOVE_FALL_LAND = 0x0C9, + MSG_MOVE_START_SWIM = 0x0CA, + MSG_MOVE_STOP_SWIM = 0x0CB, + MSG_MOVE_SET_RUN_SPEED_CHEAT = 0x0CC, + MSG_MOVE_SET_RUN_SPEED = 0x0CD, + MSG_MOVE_SET_RUN_BACK_SPEED_CHEAT = 0x0CE, + MSG_MOVE_SET_RUN_BACK_SPEED = 0x0CF, + MSG_MOVE_SET_WALK_SPEED_CHEAT = 0x0D0, + MSG_MOVE_SET_WALK_SPEED = 0x0D1, + MSG_MOVE_SET_SWIM_SPEED_CHEAT = 0x0D2, + MSG_MOVE_SET_SWIM_SPEED = 0x0D3, + MSG_MOVE_SET_SWIM_BACK_SPEED_CHEAT = 0x0D4, + MSG_MOVE_SET_SWIM_BACK_SPEED = 0x0D5, + MSG_MOVE_SET_ALL_SPEED_CHEAT = 0x0D6, + MSG_MOVE_SET_TURN_RATE_CHEAT = 0x0D7, + MSG_MOVE_SET_TURN_RATE = 0x0D8, + MSG_MOVE_TOGGLE_COLLISION_CHEAT = 0x0D9, + MSG_MOVE_SET_FACING = 0x0DA, + MSG_MOVE_SET_PITCH = 0x0DB, + MSG_MOVE_WORLDPORT_ACK = 0x0DC, + SMSG_MONSTER_MOVE = 0x0DD, + SMSG_MOVE_WATER_WALK = 0x0DE, + SMSG_MOVE_LAND_WALK = 0x0DF, + MSG_MOVE_SET_RAW_POSITION_ACK = 0x0E0, + CMSG_MOVE_SET_RAW_POSITION = 0x0E1, + SMSG_FORCE_RUN_SPEED_CHANGE = 0x0E2, + CMSG_FORCE_RUN_SPEED_CHANGE_ACK = 0x0E3, + SMSG_FORCE_RUN_BACK_SPEED_CHANGE = 0x0E4, + CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK = 0x0E5, + SMSG_FORCE_SWIM_SPEED_CHANGE = 0x0E6, + CMSG_FORCE_SWIM_SPEED_CHANGE_ACK = 0x0E7, + SMSG_FORCE_MOVE_ROOT = 0x0E8, + CMSG_FORCE_MOVE_ROOT_ACK = 0x0E9, + SMSG_FORCE_MOVE_UNROOT = 0x0EA, + CMSG_FORCE_MOVE_UNROOT_ACK = 0x0EB, + MSG_MOVE_ROOT = 0x0EC, + MSG_MOVE_UNROOT = 0x0ED, + MSG_MOVE_HEARTBEAT = 0x0EE, + SMSG_MOVE_KNOCK_BACK = 0x0EF, + CMSG_MOVE_KNOCK_BACK_ACK = 0x0F0, + MSG_MOVE_KNOCK_BACK = 0x0F1, + SMSG_MOVE_FEATHER_FALL = 0x0F2, + SMSG_MOVE_NORMAL_FALL = 0x0F3, + SMSG_MOVE_SET_HOVER = 0x0F4, + SMSG_MOVE_UNSET_HOVER = 0x0F5, + CMSG_MOVE_HOVER_ACK = 0x0F6, + MSG_MOVE_HOVER = 0x0F7, + CMSG_TRIGGER_CINEMATIC_CHEAT = 0x0F8, + CMSG_OPENING_CINEMATIC = 0x0F9, + SMSG_TRIGGER_CINEMATIC = 0x0FA, + CMSG_NEXT_CINEMATIC_CAMERA = 0x0FB, + CMSG_COMPLETE_CINEMATIC = 0x0FC, + SMSG_TUTORIAL_FLAGS = 0x0FD, + CMSG_TUTORIAL_FLAG = 0x0FE, + CMSG_TUTORIAL_CLEAR = 0x0FF, + CMSG_TUTORIAL_RESET = 0x100, + CMSG_STANDSTATECHANGE = 0x101, + CMSG_EMOTE = 0x102, + SMSG_EMOTE = 0x103, + CMSG_TEXT_EMOTE = 0x104, + SMSG_TEXT_EMOTE = 0x105, + CMSG_AUTOEQUIP_GROUND_ITEM = 0x106, + CMSG_AUTOSTORE_GROUND_ITEM = 0x107, + CMSG_AUTOSTORE_LOOT_ITEM = 0x108, + CMSG_STORE_LOOT_IN_SLOT = 0x109, + CMSG_AUTOEQUIP_ITEM = 0x10A, + CMSG_AUTOSTORE_BAG_ITEM = 0x10B, + CMSG_SWAP_ITEM = 0x10C, + CMSG_SWAP_INV_ITEM = 0x10D, + CMSG_SPLIT_ITEM = 0x10E, + CMSG_AUTOEQUIP_ITEM_SLOT = 0x10F, + OBSOLETE_DROP_ITEM = 0x110, + CMSG_DESTROYITEM = 0x111, + SMSG_INVENTORY_CHANGE_FAILURE = 0x112, + SMSG_OPEN_CONTAINER = 0x113, + CMSG_INSPECT = 0x114, + SMSG_INSPECT = 0x115, + CMSG_INITIATE_TRADE = 0x116, + CMSG_BEGIN_TRADE = 0x117, + CMSG_BUSY_TRADE = 0x118, + CMSG_IGNORE_TRADE = 0x119, + CMSG_ACCEPT_TRADE = 0x11A, + CMSG_UNACCEPT_TRADE = 0x11B, + CMSG_CANCEL_TRADE = 0x11C, + CMSG_SET_TRADE_ITEM = 0x11D, + CMSG_CLEAR_TRADE_ITEM = 0x11E, + CMSG_SET_TRADE_GOLD = 0x11F, + SMSG_TRADE_STATUS = 0x120, + SMSG_TRADE_STATUS_EXTENDED = 0x121, + SMSG_INITIALIZE_FACTIONS = 0x122, + SMSG_SET_FACTION_VISIBLE = 0x123, + SMSG_SET_FACTION_STANDING = 0x124, + CMSG_SET_FACTION_ATWAR = 0x125, + CMSG_SET_FACTION_CHEAT = 0x126, + SMSG_SET_PROFICIENCY = 0x127, + CMSG_SET_ACTION_BUTTON = 0x128, + SMSG_ACTION_BUTTONS = 0x129, + SMSG_INITIAL_SPELLS = 0x12A, + SMSG_LEARNED_SPELL = 0x12B, + SMSG_SUPERCEDED_SPELL = 0x12C, + CMSG_NEW_SPELL_SLOT = 0x12D, + CMSG_CAST_SPELL = 0x12E, + CMSG_CANCEL_CAST = 0x12F, + SMSG_CAST_FAILED = 0x130, + SMSG_SPELL_START = 0x131, + SMSG_SPELL_GO = 0x132, + SMSG_SPELL_FAILURE = 0x133, + SMSG_SPELL_COOLDOWN = 0x134, + SMSG_COOLDOWN_EVENT = 0x135, + CMSG_CANCEL_AURA = 0x136, + SMSG_UPDATE_AURA_DURATION = 0x137, + SMSG_PET_CAST_FAILED = 0x138, + MSG_CHANNEL_START = 0x139, + MSG_CHANNEL_UPDATE = 0x13A, + CMSG_CANCEL_CHANNELLING = 0x13B, + SMSG_AI_REACTION = 0x13C, + CMSG_SET_SELECTION = 0x13D, + CMSG_SET_TARGET_OBSOLETE = 0x13E, + CMSG_UNUSED = 0x13F, + CMSG_UNUSED2 = 0x140, + CMSG_ATTACKSWING = 0x141, + CMSG_ATTACKSTOP = 0x142, + SMSG_ATTACKSTART = 0x143, + SMSG_ATTACKSTOP = 0x144, + SMSG_ATTACKSWING_NOTINRANGE = 0x145, + SMSG_ATTACKSWING_BADFACING = 0x146, + SMSG_ATTACKSWING_NOTSTANDING = 0x147, + SMSG_ATTACKSWING_DEADTARGET = 0x148, + SMSG_ATTACKSWING_CANT_ATTACK = 0x149, + SMSG_ATTACKERSTATEUPDATE = 0x14A, + SMSG_VICTIMSTATEUPDATE_OBSOLETE = 0x14B, + SMSG_DAMAGE_DONE_OBSOLETE = 0x14C, + SMSG_DAMAGE_TAKEN_OBSOLETE = 0x14D, + SMSG_CANCEL_COMBAT = 0x14E, + SMSG_PLAYER_COMBAT_XP_GAIN_OBSOLETE = 0x14F, + SMSG_SPELLHEALLOG = 0x150, + SMSG_SPELLENERGIZELOG = 0x151, + CMSG_SHEATHE_OBSOLETE = 0x152, + CMSG_SAVE_PLAYER = 0x153, + CMSG_SETDEATHBINDPOINT = 0x154, + SMSG_BINDPOINTUPDATE = 0x155, + CMSG_GETDEATHBINDZONE = 0x156, + SMSG_BINDZONEREPLY = 0x157, + SMSG_PLAYERBOUND = 0x158, + SMSG_CLIENT_CONTROL_UPDATE = 0x159, + CMSG_REPOP_REQUEST = 0x15A, + SMSG_RESURRECT_REQUEST = 0x15B, + CMSG_RESURRECT_RESPONSE = 0x15C, + CMSG_LOOT = 0x15D, + CMSG_LOOT_MONEY = 0x15E, + CMSG_LOOT_RELEASE = 0x15F, + SMSG_LOOT_RESPONSE = 0x160, + SMSG_LOOT_RELEASE_RESPONSE = 0x161, + SMSG_LOOT_REMOVED = 0x162, + SMSG_LOOT_MONEY_NOTIFY = 0x163, + SMSG_LOOT_ITEM_NOTIFY = 0x164, + SMSG_LOOT_CLEAR_MONEY = 0x165, + SMSG_ITEM_PUSH_RESULT = 0x166, + SMSG_DUEL_REQUESTED = 0x167, + SMSG_DUEL_OUTOFBOUNDS = 0x168, + SMSG_DUEL_INBOUNDS = 0x169, + SMSG_DUEL_COMPLETE = 0x16A, + SMSG_DUEL_WINNER = 0x16B, + CMSG_DUEL_ACCEPTED = 0x16C, + CMSG_DUEL_CANCELLED = 0x16D, + SMSG_MOUNTRESULT = 0x16E, + SMSG_DISMOUNTRESULT = 0x16F, + SMSG_PUREMOUNT_CANCELLED_OBSOLETE = 0x170, + CMSG_MOUNTSPECIAL_ANIM = 0x171, + SMSG_MOUNTSPECIAL_ANIM = 0x172, + SMSG_PET_TAME_FAILURE = 0x173, + CMSG_PET_SET_ACTION = 0x174, + CMSG_PET_ACTION = 0x175, + CMSG_PET_ABANDON = 0x176, + CMSG_PET_RENAME = 0x177, + SMSG_PET_NAME_INVALID = 0x178, + SMSG_PET_SPELLS = 0x179, + SMSG_PET_MODE = 0x17A, + CMSG_GOSSIP_HELLO = 0x17B, + CMSG_GOSSIP_SELECT_OPTION = 0x17C, + SMSG_GOSSIP_MESSAGE = 0x17D, + SMSG_GOSSIP_COMPLETE = 0x17E, + CMSG_NPC_TEXT_QUERY = 0x17F, + SMSG_NPC_TEXT_UPDATE = 0x180, + SMSG_NPC_WONT_TALK = 0x181, + CMSG_QUESTGIVER_STATUS_QUERY = 0x182, + SMSG_QUESTGIVER_STATUS = 0x183, + CMSG_QUESTGIVER_HELLO = 0x184, + SMSG_QUESTGIVER_QUEST_LIST = 0x185, + CMSG_QUESTGIVER_QUERY_QUEST = 0x186, + CMSG_QUESTGIVER_QUEST_AUTOLAUNCH = 0x187, + SMSG_QUESTGIVER_QUEST_DETAILS = 0x188, + CMSG_QUESTGIVER_ACCEPT_QUEST = 0x189, + CMSG_QUESTGIVER_COMPLETE_QUEST = 0x18A, + SMSG_QUESTGIVER_REQUEST_ITEMS = 0x18B, + CMSG_QUESTGIVER_REQUEST_REWARD = 0x18C, + SMSG_QUESTGIVER_OFFER_REWARD = 0x18D, + CMSG_QUESTGIVER_CHOOSE_REWARD = 0x18E, + SMSG_QUESTGIVER_QUEST_INVALID = 0x18F, + CMSG_QUESTGIVER_CANCEL = 0x190, + SMSG_QUESTGIVER_QUEST_COMPLETE = 0x191, + SMSG_QUESTGIVER_QUEST_FAILED = 0x192, + CMSG_QUESTLOG_SWAP_QUEST = 0x193, + CMSG_QUESTLOG_REMOVE_QUEST = 0x194, + SMSG_QUESTLOG_FULL = 0x195, + SMSG_QUESTUPDATE_FAILED = 0x196, + SMSG_QUESTUPDATE_FAILEDTIMER = 0x197, + SMSG_QUESTUPDATE_COMPLETE = 0x198, + SMSG_QUESTUPDATE_ADD_KILL = 0x199, + SMSG_QUESTUPDATE_ADD_ITEM = 0x19A, + CMSG_QUEST_CONFIRM_ACCEPT = 0x19B, + SMSG_QUEST_CONFIRM_ACCEPT = 0x19C, + CMSG_PUSHQUESTTOPARTY = 0x19D, + CMSG_LIST_INVENTORY = 0x19E, + SMSG_LIST_INVENTORY = 0x19F, + CMSG_SELL_ITEM = 0x1A0, + SMSG_SELL_ITEM = 0x1A1, + CMSG_BUY_ITEM = 0x1A2, + CMSG_BUY_ITEM_IN_SLOT = 0x1A3, + SMSG_BUY_ITEM = 0x1A4, + SMSG_BUY_FAILED = 0x1A5, + CMSG_TAXICLEARALLNODES = 0x1A6, + CMSG_TAXIENABLEALLNODES = 0x1A7, + CMSG_TAXISHOWNODES = 0x1A8, + SMSG_SHOWTAXINODES = 0x1A9, + CMSG_TAXINODE_STATUS_QUERY = 0x1AA, + SMSG_TAXINODE_STATUS = 0x1AB, + CMSG_TAXIQUERYAVAILABLENODES = 0x1AC, + CMSG_ACTIVATETAXI = 0x1AD, + SMSG_ACTIVATETAXIREPLY = 0x1AE, + SMSG_NEW_TAXI_PATH = 0x1AF, + CMSG_TRAINER_LIST = 0x1B0, + SMSG_TRAINER_LIST = 0x1B1, + CMSG_TRAINER_BUY_SPELL = 0x1B2, + SMSG_TRAINER_BUY_SUCCEEDED = 0x1B3, + SMSG_TRAINER_BUY_FAILED = 0x1B4, + CMSG_BINDER_ACTIVATE = 0x1B5, + SMSG_PLAYERBINDERROR = 0x1B6, + CMSG_BANKER_ACTIVATE = 0x1B7, + SMSG_SHOW_BANK = 0x1B8, + CMSG_BUY_BANK_SLOT = 0x1B9, + SMSG_BUY_BANK_SLOT_RESULT = 0x1BA, + CMSG_PETITION_SHOWLIST = 0x1BB, + SMSG_PETITION_SHOWLIST = 0x1BC, + CMSG_PETITION_BUY = 0x1BD, + CMSG_PETITION_SHOW_SIGNATURES = 0x1BE, + SMSG_PETITION_SHOW_SIGNATURES = 0x1BF, + CMSG_PETITION_SIGN = 0x1C0, + SMSG_PETITION_SIGN_RESULTS = 0x1C1, + MSG_PETITION_DECLINE = 0x1C2, + CMSG_OFFER_PETITION = 0x1C3, + CMSG_TURN_IN_PETITION = 0x1C4, + SMSG_TURN_IN_PETITION_RESULTS = 0x1C5, + CMSG_PETITION_QUERY = 0x1C6, + SMSG_PETITION_QUERY_RESPONSE = 0x1C7, + SMSG_FISH_NOT_HOOKED = 0x1C8, + SMSG_FISH_ESCAPED = 0x1C9, + CMSG_BUG = 0x1CA, + SMSG_NOTIFICATION = 0x1CB, + CMSG_PLAYED_TIME = 0x1CC, + SMSG_PLAYED_TIME = 0x1CD, + CMSG_QUERY_TIME = 0x1CE, + SMSG_QUERY_TIME_RESPONSE = 0x1CF, + SMSG_LOG_XPGAIN = 0x1D0, + SMSG_AURACASTLOG = 0x1D1, + CMSG_RECLAIM_CORPSE = 0x1D2, + CMSG_WRAP_ITEM = 0x1D3, + SMSG_LEVELUP_INFO = 0x1D4, + MSG_MINIMAP_PING = 0x1D5, + SMSG_RESISTLOG = 0x1D6, + SMSG_ENCHANTMENTLOG = 0x1D7, + CMSG_SET_SKILL_CHEAT = 0x1D8, + SMSG_START_MIRROR_TIMER = 0x1D9, + SMSG_PAUSE_MIRROR_TIMER = 0x1DA, + SMSG_STOP_MIRROR_TIMER = 0x1DB, + CMSG_PING = 0x1DC, + SMSG_PONG = 0x1DD, + SMSG_CLEAR_COOLDOWN = 0x1DE, + SMSG_GAMEOBJECT_PAGETEXT = 0x1DF, + CMSG_SETSHEATHED = 0x1E0, + SMSG_COOLDOWN_CHEAT = 0x1E1, + SMSG_SPELL_DELAYED = 0x1E2, + CMSG_PLAYER_MACRO_OBSOLETE = 0x1E3, + SMSG_PLAYER_MACRO_OBSOLETE = 0x1E4, + CMSG_GHOST = 0x1E5, + CMSG_GM_INVIS = 0x1E6, + SMSG_INVALID_PROMOTION_CODE = 0x1E7, + MSG_GM_BIND_OTHER = 0x1E8, + MSG_GM_SUMMON = 0x1E9, + SMSG_ITEM_TIME_UPDATE = 0x1EA, + SMSG_ITEM_ENCHANT_TIME_UPDATE = 0x1EB, + SMSG_AUTH_CHALLENGE = 0x1EC, + CMSG_AUTH_SESSION = 0x1ED, + SMSG_AUTH_RESPONSE = 0x1EE, + MSG_GM_SHOWLABEL = 0x1EF, + CMSG_PET_CAST_SPELL = 0x1F0, + MSG_SAVE_GUILD_EMBLEM = 0x1F1, + MSG_TABARDVENDOR_ACTIVATE = 0x1F2, + SMSG_PLAY_SPELL_VISUAL = 0x1F3, + CMSG_ZONEUPDATE = 0x1F4, + SMSG_PARTYKILLLOG = 0x1F5, + SMSG_COMPRESSED_UPDATE_OBJECT = 0x1F6, + SMSG_PLAY_SPELL_IMPACT = 0x1F7, + SMSG_EXPLORATION_EXPERIENCE = 0x1F8, + CMSG_GM_SET_SECURITY_GROUP = 0x1F9, + CMSG_GM_NUKE = 0x1FA, + MSG_RANDOM_ROLL = 0x1FB, + SMSG_ENVIRONMENTALDAMAGELOG = 0x1FC, + CMSG_RWHOIS_OBSOLETE = 0x1FD, + SMSG_RWHOIS = 0x1FE, + MSG_LOOKING_FOR_GROUP = 0x1FF, + CMSG_SET_LOOKING_FOR_GROUP = 0x200, + CMSG_UNLEARN_SPELL = 0x201, + CMSG_UNLEARN_SKILL = 0x202, + SMSG_REMOVED_SPELL = 0x203, + CMSG_DECHARGE = 0x204, + CMSG_GMTICKET_CREATE = 0x205, + SMSG_GMTICKET_CREATE = 0x206, + CMSG_GMTICKET_UPDATETEXT = 0x207, + SMSG_GMTICKET_UPDATETEXT = 0x208, + SMSG_ACCOUNT_DATA_TIMES = 0x209, + CMSG_REQUEST_ACCOUNT_DATA = 0x20A, + CMSG_UPDATE_ACCOUNT_DATA = 0x20B, + SMSG_UPDATE_ACCOUNT_DATA = 0x20C, + SMSG_CLEAR_FAR_SIGHT_IMMEDIATE = 0x20D, + SMSG_POWERGAINLOG_OBSOLETE = 0x20E, + CMSG_GM_TEACH = 0x20F, + CMSG_GM_CREATE_ITEM_TARGET = 0x210, + CMSG_GMTICKET_GETTICKET = 0x211, + SMSG_GMTICKET_GETTICKET = 0x212, + CMSG_UNLEARN_TALENTS = 0x213, + SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE = 0x214, + SMSG_GAMEOBJECT_DESPAWN_ANIM = 0x215, + MSG_CORPSE_QUERY = 0x216, + CMSG_GMTICKET_DELETETICKET = 0x217, + SMSG_GMTICKET_DELETETICKET = 0x218, + SMSG_CHAT_WRONG_FACTION = 0x219, + CMSG_GMTICKET_SYSTEMSTATUS = 0x21A, + SMSG_GMTICKET_SYSTEMSTATUS = 0x21B, + CMSG_SPIRIT_HEALER_ACTIVATE = 0x21C, + CMSG_SET_STAT_CHEAT = 0x21D, + SMSG_SET_REST_START = 0x21E, + CMSG_SKILL_BUY_STEP = 0x21F, + CMSG_SKILL_BUY_RANK = 0x220, + CMSG_XP_CHEAT = 0x221, + SMSG_SPIRIT_HEALER_CONFIRM = 0x222, + CMSG_CHARACTER_POINT_CHEAT = 0x223, + SMSG_GOSSIP_POI = 0x224, + CMSG_CHAT_IGNORED = 0x225, + CMSG_GM_VISION = 0x226, + CMSG_SERVER_COMMAND = 0x227, + CMSG_GM_SILENCE = 0x228, + CMSG_GM_REVEALTO = 0x229, + CMSG_GM_RESURRECT = 0x22A, + CMSG_GM_SUMMONMOB = 0x22B, + CMSG_GM_MOVECORPSE = 0x22C, + CMSG_GM_FREEZE = 0x22D, + CMSG_GM_UBERINVIS = 0x22E, + CMSG_GM_REQUEST_PLAYER_INFO = 0x22F, + SMSG_GM_PLAYER_INFO = 0x230, + CMSG_GUILD_RANK = 0x231, + CMSG_GUILD_ADD_RANK = 0x232, + CMSG_GUILD_DEL_RANK = 0x233, + CMSG_GUILD_SET_PUBLIC_NOTE = 0x234, + CMSG_GUILD_SET_OFFICER_NOTE = 0x235, + SMSG_LOGIN_VERIFY_WORLD = 0x236, + CMSG_CLEAR_EXPLORATION = 0x237, + CMSG_SEND_MAIL = 0x238, + SMSG_SEND_MAIL_RESULT = 0x239, + CMSG_GET_MAIL_LIST = 0x23A, + SMSG_MAIL_LIST_RESULT = 0x23B, + CMSG_BATTLEFIELD_LIST = 0x23C, + SMSG_BATTLEFIELD_LIST = 0x23D, + CMSG_BATTLEFIELD_JOIN = 0x23E, + SMSG_BATTLEFIELD_WIN_OBSOLETE = 0x23F, + SMSG_BATTLEFIELD_LOSE_OBSOLETE = 0x240, + CMSG_TAXICLEARNODE = 0x241, + CMSG_TAXIENABLENODE = 0x242, + CMSG_ITEM_TEXT_QUERY = 0x243, + SMSG_ITEM_TEXT_QUERY_RESPONSE = 0x244, + CMSG_MAIL_TAKE_MONEY = 0x245, + CMSG_MAIL_TAKE_ITEM = 0x246, + CMSG_MAIL_MARK_AS_READ = 0x247, + CMSG_MAIL_RETURN_TO_SENDER = 0x248, + CMSG_MAIL_DELETE = 0x249, + CMSG_MAIL_CREATE_TEXT_ITEM = 0x24A, + SMSG_SPELLLOGMISS = 0x24B, + SMSG_SPELLLOGEXECUTE = 0x24C, + SMSG_DEBUGAURAPROC = 0x24D, + SMSG_PERIODICAURALOG = 0x24E, + SMSG_SPELLDAMAGESHIELD = 0x24F, + SMSG_SPELLNONMELEEDAMAGELOG = 0x250, + CMSG_LEARN_TALENT = 0x251, + SMSG_RESURRECT_FAILED = 0x252, + CMSG_TOGGLE_PVP = 0x253, + SMSG_ZONE_UNDER_ATTACK = 0x254, + MSG_AUCTION_HELLO = 0x255, + CMSG_AUCTION_SELL_ITEM = 0x256, + CMSG_AUCTION_REMOVE_ITEM = 0x257, + CMSG_AUCTION_LIST_ITEMS = 0x258, + CMSG_AUCTION_LIST_OWNER_ITEMS = 0x259, + CMSG_AUCTION_PLACE_BID = 0x25A, + SMSG_AUCTION_COMMAND_RESULT = 0x25B, + SMSG_AUCTION_LIST_RESULT = 0x25C, + SMSG_AUCTION_OWNER_LIST_RESULT = 0x25D, + SMSG_AUCTION_BIDDER_NOTIFICATION = 0x25E, + SMSG_AUCTION_OWNER_NOTIFICATION = 0x25F, + SMSG_PROCRESIST = 0x260, + SMSG_STANDSTATE_CHANGE_FAILURE_OBSOLETE = 0x261, + SMSG_DISPEL_FAILED = 0x262, + SMSG_SPELLORDAMAGE_IMMUNE = 0x263, + CMSG_AUCTION_LIST_BIDDER_ITEMS = 0x264, + SMSG_AUCTION_BIDDER_LIST_RESULT = 0x265, + SMSG_SET_FLAT_SPELL_MODIFIER = 0x266, + SMSG_SET_PCT_SPELL_MODIFIER = 0x267, + CMSG_SET_AMMO = 0x268, + SMSG_CORPSE_RECLAIM_DELAY = 0x269, + CMSG_SET_ACTIVE_MOVER = 0x26A, + CMSG_PET_CANCEL_AURA = 0x26B, + CMSG_PLAYER_AI_CHEAT = 0x26C, + CMSG_CANCEL_AUTO_REPEAT_SPELL = 0x26D, + MSG_GM_ACCOUNT_ONLINE = 0x26E, + MSG_LIST_STABLED_PETS = 0x26F, + CMSG_STABLE_PET = 0x270, + CMSG_UNSTABLE_PET = 0x271, + CMSG_BUY_STABLE_SLOT = 0x272, + SMSG_STABLE_RESULT = 0x273, + CMSG_STABLE_REVIVE_PET = 0x274, + CMSG_STABLE_SWAP_PET = 0x275, + MSG_QUEST_PUSH_RESULT = 0x276, + SMSG_PLAY_MUSIC = 0x277, + SMSG_PLAY_OBJECT_SOUND = 0x278, + CMSG_REQUEST_PET_INFO = 0x279, + CMSG_FAR_SIGHT = 0x27A, + SMSG_SPELLDISPELLOG = 0x27B, + SMSG_DAMAGE_CALC_LOG = 0x27C, + CMSG_ENABLE_DAMAGE_LOG = 0x27D, + CMSG_GROUP_CHANGE_SUB_GROUP = 0x27E, + CMSG_REQUEST_PARTY_MEMBER_STATS = 0x27F, + CMSG_GROUP_SWAP_SUB_GROUP = 0x280, + CMSG_RESET_FACTION_CHEAT = 0x281, + CMSG_AUTOSTORE_BANK_ITEM = 0x282, + CMSG_AUTOBANK_ITEM = 0x283, + MSG_QUERY_NEXT_MAIL_TIME = 0x284, + SMSG_RECEIVED_MAIL = 0x285, + SMSG_RAID_GROUP_ONLY = 0x286, + CMSG_SET_DURABILITY_CHEAT = 0x287, + CMSG_SET_PVP_RANK_CHEAT = 0x288, + CMSG_ADD_PVP_MEDAL_CHEAT = 0x289, + CMSG_DEL_PVP_MEDAL_CHEAT = 0x28A, + CMSG_SET_PVP_TITLE = 0x28B, + SMSG_PVP_CREDIT = 0x28C, + SMSG_AUCTION_REMOVED_NOTIFICATION = 0x28D, + CMSG_GROUP_RAID_CONVERT = 0x28E, + CMSG_GROUP_ASSISTANT_LEADER = 0x28F, + CMSG_BUYBACK_ITEM = 0x290, + SMSG_SERVER_MESSAGE = 0x291, + CMSG_MEETINGSTONE_JOIN = 0x292, + CMSG_MEETINGSTONE_LEAVE = 0x293, + CMSG_MEETINGSTONE_CHEAT = 0x294, + SMSG_MEETINGSTONE_SETQUEUE = 0x295, + CMSG_MEETINGSTONE_INFO = 0x296, + SMSG_MEETINGSTONE_COMPLETE = 0x297, + SMSG_MEETINGSTONE_IN_PROGRESS = 0x298, + SMSG_MEETINGSTONE_MEMBER_ADDED = 0x299, + CMSG_GMTICKETSYSTEM_TOGGLE = 0x29A, + CMSG_CANCEL_GROWTH_AURA = 0x29B, + SMSG_CANCEL_AUTO_REPEAT = 0x29C, + SMSG_STANDSTATE_UPDATE = 0x29D, + SMSG_LOOT_ALL_PASSED = 0x29E, + SMSG_LOOT_ROLL_WON = 0x29F, + CMSG_LOOT_ROLL = 0x2A0, + SMSG_LOOT_START_ROLL = 0x2A1, + SMSG_LOOT_ROLL = 0x2A2, + CMSG_LOOT_MASTER_GIVE = 0x2A3, + SMSG_LOOT_MASTER_LIST = 0x2A4, + SMSG_SET_FORCED_REACTIONS = 0x2A5, + SMSG_SPELL_FAILED_OTHER = 0x2A6, + SMSG_GAMEOBJECT_RESET_STATE = 0x2A7, + CMSG_REPAIR_ITEM = 0x2A8, + SMSG_CHAT_PLAYER_NOT_FOUND = 0x2A9, + MSG_TALENT_WIPE_CONFIRM = 0x2AA, + SMSG_SUMMON_REQUEST = 0x2AB, + CMSG_SUMMON_RESPONSE = 0x2AC, + MSG_MOVE_TOGGLE_GRAVITY_CHEAT = 0x2AD, + SMSG_MONSTER_MOVE_TRANSPORT = 0x2AE, + SMSG_PET_BROKEN = 0x2AF, + MSG_MOVE_FEATHER_FALL = 0x2B0, + MSG_MOVE_WATER_WALK = 0x2B1, + CMSG_SERVER_BROADCAST = 0x2B2, + CMSG_SELF_RES = 0x2B3, + SMSG_FEIGN_DEATH_RESISTED = 0x2B4, + CMSG_RUN_SCRIPT = 0x2B5, + SMSG_SCRIPT_MESSAGE = 0x2B6, + SMSG_DUEL_COUNTDOWN = 0x2B7, + SMSG_AREA_TRIGGER_MESSAGE = 0x2B8, + CMSG_TOGGLE_HELM = 0x2B9, + CMSG_TOGGLE_CLOAK = 0x2BA, + SMSG_MEETINGSTONE_JOINFAILED = 0x2BB, + SMSG_PLAYER_SKINNED = 0x2BC, + SMSG_DURABILITY_DAMAGE_DEATH = 0x2BD, + CMSG_SET_EXPLORATION = 0x2BE, + CMSG_SET_ACTIONBAR_TOGGLES = 0x2BF, + UMSG_DELETE_GUILD_CHARTER = 0x2C0, + MSG_PETITION_RENAME = 0x2C1, + SMSG_INIT_WORLD_STATES = 0x2C2, + SMSG_UPDATE_WORLD_STATE = 0x2C3, + CMSG_ITEM_NAME_QUERY = 0x2C4, + SMSG_ITEM_NAME_QUERY_RESPONSE = 0x2C5, + SMSG_PET_ACTION_FEEDBACK = 0x2C6, + CMSG_CHAR_RENAME = 0x2C7, + SMSG_CHAR_RENAME = 0x2C8, + CMSG_MOVE_SPLINE_DONE = 0x2C9, + CMSG_MOVE_FALL_RESET = 0x2CA, + SMSG_INSTANCE_SAVE_CREATED = 0x2CB, + SMSG_RAID_INSTANCE_INFO = 0x2CC, + CMSG_REQUEST_RAID_INFO = 0x2CD, + CMSG_MOVE_TIME_SKIPPED = 0x2CE, + CMSG_MOVE_FEATHER_FALL_ACK = 0x2CF, + CMSG_MOVE_WATER_WALK_ACK = 0x2D0, + CMSG_MOVE_NOT_ACTIVE_MOVER = 0x2D1, + SMSG_PLAY_SOUND = 0x2D2, + CMSG_BATTLEFIELD_STATUS = 0x2D3, + SMSG_BATTLEFIELD_STATUS = 0x2D4, + CMSG_BATTLEFIELD_PORT = 0x2D5, + MSG_INSPECT_HONOR_STATS = 0x2D6, + CMSG_BATTLEMASTER_HELLO = 0x2D7, + CMSG_MOVE_START_SWIM_CHEAT = 0x2D8, + CMSG_MOVE_STOP_SWIM_CHEAT = 0x2D9, + SMSG_FORCE_WALK_SPEED_CHANGE = 0x2DA, + CMSG_FORCE_WALK_SPEED_CHANGE_ACK = 0x2DB, + SMSG_FORCE_SWIM_BACK_SPEED_CHANGE = 0x2DC, + CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK = 0x2DD, + SMSG_FORCE_TURN_RATE_CHANGE = 0x2DE, + CMSG_FORCE_TURN_RATE_CHANGE_ACK = 0x2DF, + MSG_PVP_LOG_DATA = 0x2E0, + CMSG_LEAVE_BATTLEFIELD = 0x2E1, + CMSG_AREA_SPIRIT_HEALER_QUERY = 0x2E2, + CMSG_AREA_SPIRIT_HEALER_QUEUE = 0x2E3, + SMSG_AREA_SPIRIT_HEALER_TIME = 0x2E4, + CMSG_GM_UNTEACH = 0x2E5, + SMSG_WARDEN_DATA = 0x2E6, + CMSG_WARDEN_DATA = 0x2E7, + SMSG_GROUP_JOINED_BATTLEGROUND = 0x2E8, + MSG_BATTLEGROUND_PLAYER_POSITIONS = 0x2E9, + CMSG_PET_STOP_ATTACK = 0x2EA, + SMSG_BINDER_CONFIRM = 0x2EB, + SMSG_BATTLEGROUND_PLAYER_JOINED = 0x2EC, + SMSG_BATTLEGROUND_PLAYER_LEFT = 0x2ED, + CMSG_BATTLEMASTER_JOIN = 0x2EE, + SMSG_ADDON_INFO = 0x2EF, + CMSG_PET_UNLEARN = 0x2F0, + SMSG_PET_UNLEARN_CONFIRM = 0x2F1, + SMSG_PARTY_MEMBER_STATS_FULL = 0x2F2, + CMSG_PET_SPELL_AUTOCAST = 0x2F3, + SMSG_WEATHER = 0x2F4, + SMSG_PLAY_TIME_WARNING = 0x2F5, + SMSG_MINIGAME_SETUP = 0x2F6, + SMSG_MINIGAME_STATE = 0x2F7, + CMSG_MINIGAME_MOVE = 0x2F8, + SMSG_MINIGAME_MOVE_FAILED = 0x2F9, + SMSG_RAID_INSTANCE_MESSAGE = 0x2FA, + SMSG_COMPRESSED_MOVES = 0x2FB, + CMSG_GUILD_INFO_TEXT = 0x2FC, + SMSG_CHAT_RESTRICTED = 0x2FD, + SMSG_SPLINE_SET_RUN_SPEED = 0x2FE, + SMSG_SPLINE_SET_RUN_BACK_SPEED = 0x2FF, + SMSG_SPLINE_SET_SWIM_SPEED = 0x300, + SMSG_SPLINE_SET_WALK_SPEED = 0x301, + SMSG_SPLINE_SET_SWIM_BACK_SPEED = 0x302, + SMSG_SPLINE_SET_TURN_RATE = 0x303, + SMSG_SPLINE_MOVE_UNROOT = 0x304, + SMSG_SPLINE_MOVE_FEATHER_FALL = 0x305, + SMSG_SPLINE_MOVE_NORMAL_FALL = 0x306, + SMSG_SPLINE_MOVE_SET_HOVER = 0x307, + SMSG_SPLINE_MOVE_UNSET_HOVER = 0x308, + SMSG_SPLINE_MOVE_WATER_WALK = 0x309, + SMSG_SPLINE_MOVE_LAND_WALK = 0x30A, + SMSG_SPLINE_MOVE_START_SWIM = 0x30B, + SMSG_SPLINE_MOVE_STOP_SWIM = 0x30C, + SMSG_SPLINE_MOVE_SET_RUN_MODE = 0x30D, + SMSG_SPLINE_MOVE_SET_WALK_MODE = 0x30E, + CMSG_GM_NUKE_ACCOUNT = 0x30F, + MSG_GM_DESTROY_CORPSE = 0x310, + CMSG_GM_DESTROY_ONLINE_CORPSE = 0x311, + CMSG_ACTIVATETAXIEXPRESS = 0x312, + SMSG_SET_FACTION_ATWAR = 0x313, + SMSG_GAMETIMEBIAS_SET = 0x314, + CMSG_DEBUG_ACTIONS_START = 0x315, + CMSG_DEBUG_ACTIONS_STOP = 0x316, + CMSG_SET_FACTION_INACTIVE = 0x317, + CMSG_SET_WATCHED_FACTION = 0x318, + MSG_MOVE_TIME_SKIPPED = 0x319, + SMSG_SPLINE_MOVE_ROOT = 0x31A, + CMSG_SET_EXPLORATION_ALL = 0x31B, + SMSG_INVALIDATE_PLAYER = 0x31C, + CMSG_RESET_INSTANCES = 0x31D, + SMSG_INSTANCE_RESET = 0x31E, + SMSG_INSTANCE_RESET_FAILED = 0x31F, + SMSG_UPDATE_LAST_INSTANCE = 0x320, + MSG_RAID_TARGET_UPDATE = 0x321, + MSG_RAID_READY_CHECK = 0x322, + CMSG_LUA_USAGE = 0x323, + SMSG_PET_ACTION_SOUND = 0x324, + SMSG_PET_DISMISS_SOUND = 0x325, + SMSG_GHOSTEE_GONE = 0x326, + CMSG_GM_UPDATE_TICKET_STATUS = 0x327, + SMSG_GM_TICKET_STATUS_UPDATE = 0x328, + MSG_SET_DUNGEON_DIFFICULTY = 0x329, + CMSG_GMSURVEY_SUBMIT = 0x32A, + SMSG_UPDATE_INSTANCE_OWNERSHIP = 0x32B, + CMSG_IGNORE_KNOCKBACK_CHEAT = 0x32C, + SMSG_CHAT_PLAYER_AMBIGUOUS = 0x32D, + MSG_DELAY_GHOST_TELEPORT = 0x32E, + SMSG_SPELLINSTAKILLLOG = 0x32F, + SMSG_SPELL_UPDATE_CHAIN_TARGETS = 0x330, + CMSG_CHAT_FILTERED = 0x331, + SMSG_EXPECTED_SPAM_RECORDS = 0x332, + SMSG_SPELLSTEALLOG = 0x333, + CMSG_LOTTERY_QUERY_OBSOLETE = 0x334, + SMSG_LOTTERY_QUERY_RESULT_OBSOLETE = 0x335, + CMSG_BUY_LOTTERY_TICKET_OBSOLETE = 0x336, + SMSG_LOTTERY_RESULT_OBSOLETE = 0x337, + SMSG_CHARACTER_PROFILE = 0x338, + SMSG_CHARACTER_PROFILE_REALM_CONNECTED = 0x339, + SMSG_DEFENSE_MESSAGE = 0x33A, + SMSG_INSTANCE_DIFFICULTY = 0x33B, + MSG_GM_RESETINSTANCELIMIT = 0x33C, + SMSG_MOTD = 0x33D, + SMSG_MOVE_SET_FLIGHT_OBSOLETE = 0x33E, + SMSG_MOVE_UNSET_FLIGHT_OBSOLETE = 0x33F, + CMSG_MOVE_FLIGHT_ACK_OBSOLETE = 0x340, + MSG_MOVE_START_SWIM_CHEAT = 0x341, + MSG_MOVE_STOP_SWIM_CHEAT = 0x342, + SMSG_MOVE_SET_CAN_FLY = 0x343, + SMSG_MOVE_UNSET_CAN_FLY = 0x344, + CMSG_MOVE_SET_CAN_FLY_ACK = 0x345, + CMSG_MOVE_SET_FLY = 0x346, + CMSG_SOCKET_GEMS = 0x347, + CMSG_ARENA_TEAM_CREATE = 0x348, + SMSG_ARENA_TEAM_COMMAND_RESULT = 0x349, + UMSG_UPDATE_ARENA_TEAM_OBSOLETE = 0x34A, + CMSG_ARENA_TEAM_QUERY = 0x34B, + SMSG_ARENA_TEAM_QUERY_RESPONSE = 0x34C, + CMSG_ARENA_TEAM_ROSTER = 0x34D, + SMSG_ARENA_TEAM_ROSTER = 0x34E, + CMSG_ARENA_TEAM_INVITE = 0x34F, + SMSG_ARENA_TEAM_INVITE = 0x350, + CMSG_ARENA_TEAM_ACCEPT = 0x351, + CMSG_ARENA_TEAM_DECLINE = 0x352, + CMSG_ARENA_TEAM_LEAVE = 0x353, + CMSG_ARENA_TEAM_REMOVE = 0x354, + CMSG_ARENA_TEAM_DISBAND = 0x355, + CMSG_ARENA_TEAM_LEADER = 0x356, + SMSG_ARENA_TEAM_EVENT = 0x357, + CMSG_BATTLEMASTER_JOIN_ARENA = 0x358, + MSG_MOVE_START_ASCEND = 0x359, + MSG_MOVE_STOP_ASCEND = 0x35A, + SMSG_ARENA_TEAM_STATS = 0x35B, + CMSG_LFG_SET_AUTOJOIN = 0x35C, + CMSG_LFG_CLEAR_AUTOJOIN = 0x35D, + CMSG_LFM_SET_AUTOFILL = 0x35E, + CMSG_LFM_CLEAR_AUTOFILL = 0x35F, + CMSG_ACCEPT_LFG_MATCH = 0x360, + CMSG_DECLINE_LFG_MATCH = 0x361, + CMSG_CANCEL_PENDING_LFG = 0x362, + CMSG_CLEAR_LOOKING_FOR_GROUP = 0x363, + CMSG_CLEAR_LOOKING_FOR_MORE = 0x364, + CMSG_SET_LOOKING_FOR_MORE = 0x365, + CMSG_SET_LFG_COMMENT = 0x366, + SMSG_LFG_TIMEDOUT = 0x367, + SMSG_LFG_OTHER_TIMEDOUT = 0x368, + SMSG_LFG_AUTOJOIN_FAILED = 0x369, + SMSG_LFG_AUTOJOIN_FAILED_NO_PLAYER = 0x36A, + SMSG_LFG_LEADER_IS_LFM = 0x36B, + SMSG_LFG_UPDATE = 0x36C, + SMSG_LFG_UPDATE_LFM = 0x36D, + SMSG_LFG_UPDATE_LFG = 0x36E, + SMSG_LFG_UPDATE_QUEUED = 0x36F, + SMSG_LFG_PENDING_INVITE = 0x370, + SMSG_LFG_PENDING_MATCH = 0x371, + SMSG_LFG_PENDING_MATCH_DONE = 0x372, + SMSG_TITLE_EARNED = 0x373, + CMSG_SET_TITLE = 0x374, + CMSG_CANCEL_MOUNT_AURA = 0x375, + SMSG_ARENA_ERROR = 0x376, + MSG_INSPECT_ARENA_TEAMS = 0x377, + SMSG_DEATH_RELEASE_LOC = 0x378, + CMSG_CANCEL_TEMP_ENCHANTMENT = 0x379, + SMSG_FORCED_DEATH_UPDATE = 0x37A, + CMSG_CHEAT_SET_HONOR_CURRENCY = 0x37B, + CMSG_CHEAT_SET_ARENA_CURRENCY = 0x37C, + MSG_MOVE_SET_FLIGHT_SPEED_CHEAT = 0x37D, + MSG_MOVE_SET_FLIGHT_SPEED = 0x37E, + MSG_MOVE_SET_FLIGHT_BACK_SPEED_CHEAT = 0x37F, + MSG_MOVE_SET_FLIGHT_BACK_SPEED = 0x380, + SMSG_FORCE_FLIGHT_SPEED_CHANGE = 0x381, + CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK = 0x382, + SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE = 0x383, + CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK = 0x384, + SMSG_SPLINE_SET_FLIGHT_SPEED = 0x385, + SMSG_SPLINE_SET_FLIGHT_BACK_SPEED = 0x386, + CMSG_MAELSTROM_INVALIDATE_CACHE = 0x387, + SMSG_FLIGHT_SPLINE_SYNC = 0x388, + CMSG_SET_TAXI_BENCHMARK_MODE = 0x389, + SMSG_JOINED_BATTLEGROUND_QUEUE = 0x38A, + SMSG_REALM_SPLIT = 0x38B, + CMSG_REALM_SPLIT = 0x38C, + CMSG_MOVE_CHNG_TRANSPORT = 0x38D, + MSG_PARTY_ASSIGNMENT = 0x38E, + SMSG_OFFER_PETITION_ERROR = 0x38F, + SMSG_TIME_SYNC_REQ = 0x390, + CMSG_TIME_SYNC_RESP = 0x391, + CMSG_SEND_LOCAL_EVENT = 0x392, + CMSG_SEND_GENERAL_TRIGGER = 0x393, + CMSG_SEND_COMBAT_TRIGGER = 0x394, + CMSG_MAELSTROM_GM_SENT_MAIL = 0x395, + SMSG_RESET_FAILED_NOTIFY = 0x396, + SMSG_REAL_GROUP_UPDATE = 0x397, + SMSG_LFG_DISABLED = 0x398, + CMSG_ACTIVE_PVP_CHEAT = 0x399, + CMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY = 0x39A, + SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE = 0x39B, + SMSG_CHEAT_DUMP_ITEMS_DEBUG_ONLY_RESPONSE_WRITE_FILE = 0x39C, + SMSG_UPDATE_COMBO_POINTS = 0x39D, + SMSG_VOICE_SESSION_ROSTER_UPDATE = 0x39E, + SMSG_VOICE_SESSION_LEAVE = 0x39F, + SMSG_VOICE_SESSION_ADJUST_PRIORITY = 0x3A0, + CMSG_VOICE_SET_TALKER_MUTED_REQUEST = 0x3A1, + SMSG_VOICE_SET_TALKER_MUTED = 0x3A2, + SMSG_INIT_EXTRA_AURA_INFO = 0x3A3, + SMSG_SET_EXTRA_AURA_INFO = 0x3A4, + SMSG_SET_EXTRA_AURA_INFO_NEED_UPDATE = 0x3A5, + SMSG_CLEAR_EXTRA_AURA_INFO = 0x3A6, + MSG_MOVE_START_DESCEND = 0x3A7, + CMSG_IGNORE_REQUIREMENTS_CHEAT = 0x3A8, + SMSG_IGNORE_REQUIREMENTS_CHEAT = 0x3A9, + SMSG_SPELL_CHANCE_PROC_LOG = 0x3AA, + CMSG_MOVE_SET_RUN_SPEED = 0x3AB, + SMSG_DISMOUNT = 0x3AC, + MSG_MOVE_UPDATE_CAN_FLY = 0x3AD, + MSG_RAID_READY_CHECK_CONFIRM = 0x3AE, + CMSG_VOICE_SESSION_ENABLE = 0x3AF, + SMSG_VOICE_PARENTAL_CONTROLS = 0x3B0, + CMSG_GM_WHISPER = 0x3B1, + SMSG_GM_MESSAGECHAT = 0x3B2, + MSG_GM_GEARRATING = 0x3B3, + CMSG_COMMENTATOR_ENABLE = 0x3B4, + SMSG_COMMENTATOR_STATE_CHANGED = 0x3B5, + CMSG_COMMENTATOR_GET_MAP_INFO = 0x3B6, + SMSG_COMMENTATOR_MAP_INFO = 0x3B7, + CMSG_COMMENTATOR_GET_PLAYER_INFO = 0x3B8, + SMSG_COMMENTATOR_GET_PLAYER_INFO = 0x3B9, + SMSG_COMMENTATOR_PLAYER_INFO = 0x3BA, + CMSG_COMMENTATOR_ENTER_INSTANCE = 0x3BB, + CMSG_COMMENTATOR_EXIT_INSTANCE = 0x3BC, + CMSG_COMMENTATOR_INSTANCE_COMMAND = 0x3BD, + SMSG_CLEAR_TARGET = 0x3BE, + CMSG_BOT_DETECTED = 0x3BF, + SMSG_CROSSED_INEBRIATION_THRESHOLD = 0x3C0, + CMSG_CHEAT_PLAYER_LOGIN = 0x3C1, + CMSG_CHEAT_PLAYER_LOOKUP = 0x3C2, + SMSG_CHEAT_PLAYER_LOOKUP = 0x3C3, + SMSG_KICK_REASON = 0x3C4, + MSG_RAID_READY_CHECK_FINISHED = 0x3C5, + CMSG_COMPLAIN = 0x3C6, + SMSG_COMPLAIN_RESULT = 0x3C7, + SMSG_FEATURE_SYSTEM_STATUS = 0x3C8, + CMSG_GM_SHOW_COMPLAINTS = 0x3C9, + CMSG_GM_UNSQUELCH = 0x3CA, + CMSG_CHANNEL_SILENCE_VOICE = 0x3CB, + CMSG_CHANNEL_SILENCE_ALL = 0x3CC, + CMSG_CHANNEL_UNSILENCE_VOICE = 0x3CD, + CMSG_CHANNEL_UNSILENCE_ALL = 0x3CE, + CMSG_TARGET_CAST = 0x3CF, + CMSG_TARGET_SCRIPT_CAST = 0x3D0, + CMSG_CHANNEL_DISPLAY_LIST = 0x3D1, + CMSG_SET_ACTIVE_VOICE_CHANNEL = 0x3D2, + CMSG_GET_CHANNEL_MEMBER_COUNT = 0x3D3, + SMSG_CHANNEL_MEMBER_COUNT = 0x3D4, + CMSG_CHANNEL_VOICE_ON = 0x3D5, + CMSG_CHANNEL_VOICE_OFF = 0x3D6, + CMSG_DEBUG_LIST_TARGETS = 0x3D7, + SMSG_DEBUG_LIST_TARGETS = 0x3D8, + SMSG_AVAILABLE_VOICE_CHANNEL = 0x3D9, + CMSG_ADD_VOICE_IGNORE = 0x3DA, + CMSG_DEL_VOICE_IGNORE = 0x3DB, + CMSG_PARTY_SILENCE = 0x3DC, + CMSG_PARTY_UNSILENCE = 0x3DD, + MSG_NOTIFY_PARTY_SQUELCH = 0x3DE, + SMSG_COMSAT_RECONNECT_TRY = 0x3DF, + SMSG_COMSAT_DISCONNECT = 0x3E0, + SMSG_COMSAT_CONNECT_FAIL = 0x3E1, + SMSG_VOICE_CHAT_STATUS = 0x3E2, + CMSG_REPORT_PVP_AFK = 0x3E3, + CMSG_REPORT_PVP_AFK_RESULT = 0x3E4, + CMSG_GUILD_BANKER_ACTIVATE = 0x3E5, + CMSG_GUILD_BANK_QUERY_TAB = 0x3E6, + SMSG_GUILD_BANK_LIST = 0x3E7, + CMSG_GUILD_BANK_SWAP_ITEMS = 0x3E8, + CMSG_GUILD_BANK_BUY_TAB = 0x3E9, + CMSG_GUILD_BANK_UPDATE_TAB = 0x3EA, + CMSG_GUILD_BANK_DEPOSIT_MONEY = 0x3EB, + CMSG_GUILD_BANK_WITHDRAW_MONEY = 0x3EC, + MSG_GUILD_BANK_LOG_QUERY = 0x3ED, + CMSG_SET_CHANNEL_WATCH = 0x3EE, + SMSG_USERLIST_ADD = 0x3EF, + SMSG_USERLIST_REMOVE = 0x3F0, + SMSG_USERLIST_UPDATE = 0x3F1, + CMSG_CLEAR_CHANNEL_WATCH = 0x3F2, + SMSG_INSPECT_TALENT = 0x3F3, + SMSG_GOGOGO_OBSOLETE = 0x3F4, + SMSG_ECHO_PARTY_SQUELCH = 0x3F5, + CMSG_SET_TITLE_SUFFIX = 0x3F6, + CMSG_SPELLCLICK = 0x3F7, + SMSG_LOOT_LIST = 0x3F8, + CMSG_GM_CHARACTER_RESTORE = 0x3F9, + CMSG_GM_CHARACTER_SAVE = 0x3FA, + SMSG_VOICESESSION_FULL = 0x3FB, + MSG_GUILD_PERMISSIONS = 0x3FC, + MSG_GUILD_BANK_MONEY_WITHDRAWN = 0x3FD, + MSG_GUILD_EVENT_LOG_QUERY = 0x3FE, + CMSG_MAELSTROM_RENAME_GUILD = 0x3FF, + CMSG_GET_MIRRORIMAGE_DATA = 0x400, + SMSG_MIRRORIMAGE_DATA = 0x401, + SMSG_FORCE_DISPLAY_UPDATE = 0x402, + SMSG_SPELL_CHANCE_RESIST_PUSHBACK = 0x403, + CMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0x404, + SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0x405, + CMSG_KEEP_ALIVE = 0x406, + SMSG_RAID_READY_CHECK_ERROR = 0x407, + CMSG_OPT_OUT_OF_LOOT = 0x408, + MSG_QUERY_GUILD_BANK_TEXT = 0x409, + CMSG_SET_GUILD_BANK_TEXT = 0x40A, + CMSG_SET_GRANTABLE_LEVELS = 0x40B, + CMSG_GRANT_LEVEL = 0x40C, + CMSG_REFER_A_FRIEND = 0x40D, + MSG_GM_CHANGE_ARENA_RATING = 0x40E, + CMSG_DECLINE_CHANNEL_INVITE = 0x40F, + CMSG_GROUPACTION_THROTTLED = 0x410, + SMSG_OVERRIDE_LIGHT = 0x411, + SMSG_TOTEM_CREATED = 0x412, + CMSG_TOTEM_DESTROYED = 0x413, + CMSG_EXPIRE_RAID_INSTANCE = 0x414, + CMSG_NO_SPELL_VARIANCE = 0x415, + CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY = 0x416, + SMSG_QUESTGIVER_STATUS_MULTIPLE = 0x417, + CMSG_SET_PLAYER_DECLINED_NAMES = 0x418, + SMSG_SET_PLAYER_DECLINED_NAMES_RESULT = 0x419, + CMSG_QUERY_SERVER_BUCK_DATA = 0x41A, + CMSG_CLEAR_SERVER_BUCK_DATA = 0x41B, + SMSG_SERVER_BUCK_DATA = 0x41C, + SMSG_SEND_UNLEARN_SPELLS = 0x41D, + SMSG_PROPOSE_LEVEL_GRANT = 0x41E, + CMSG_ACCEPT_LEVEL_GRANT = 0x41F, + SMSG_REFER_A_FRIEND_FAILURE = 0x420, + SMSG_SPLINE_MOVE_SET_FLYING = 0x421, + SMSG_SPLINE_MOVE_UNSET_FLYING = 0x422, + SMSG_SUMMON_CANCEL = 0x423 +}; + +// Don't forget to change this value and add opcode name to Opcodes.cpp when you add new opcode! +#define NUM_MSG_TYPES 0x424 + +/// Player state +enum SessionStatus +{ + STATUS_AUTHED = 0, ///< Player authenticated + STATUS_LOGGEDIN, ///< Player in game + STATUS_TRANSFER_PENDING, ///< Player transferring to another map + STATUS_NEVER ///< Opcode not accepted from client (deprecated or server side only) +}; + +class WorldSession; +class WorldPacket; + +struct OpcodeHandler +{ + char const* name; + SessionStatus status; + void (WorldSession::*handler)(WorldPacket& recvPacket); +}; + +extern OpcodeHandler opcodeTable[NUM_MSG_TYPES]; + +/// Lookup opcode name for human understandable logging +inline const char* LookupOpcodeName(uint16 id) +{ + if (id >= NUM_MSG_TYPES) + return "Received unknown opcode, it's more than max!"; + return opcodeTable[id].name; +} +#endif +/// @} diff --git a/src/game/Path.h b/src/game/Path.h new file mode 100644 index 00000000000..2085e567def --- /dev/null +++ b/src/game/Path.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_PATH_H +#define MANGOSSERVER_PATH_H + +#include "Common.h" +#include + +class Path +{ + public: + struct PathNode + { + float x,y,z; + }; + + void SetLength(const unsigned int sz) + { + i_nodes.resize( sz ); + } + + unsigned int Size() const { return i_nodes.size(); } + bool Empty() const { return i_nodes.empty(); } + void Resize(unsigned int sz) { i_nodes.resize(sz); } + void Clear(void) { i_nodes.clear(); } + PathNode const* GetNodes(uint32 start = 0) const { return &i_nodes[start]; } + float GetTotalLength() const { return GetTotalLength(0,Size()); } + float GetTotalLength(uint32 start, uint32 end) const + { + float len = 0, xd, yd, zd; + for(unsigned int idx=start+1; idx < end; ++idx) + { + xd = i_nodes[ idx ].x - i_nodes[ idx-1 ].x; + yd = i_nodes[ idx ].y - i_nodes[ idx-1 ].y; + zd = i_nodes[ idx ].z - i_nodes[ idx-1 ].z; + len += sqrtf( xd*xd + yd*yd + zd*zd ); + } + return len; + } + + float GetPassedLength(uint32 curnode, float x, float y, float z) + { + float len = 0, xd, yd, zd; + for(unsigned int idx=1; idx < curnode; ++idx) + { + xd = i_nodes[ idx ].x - i_nodes[ idx-1 ].x; + yd = i_nodes[ idx ].y - i_nodes[ idx-1 ].y; + zd = i_nodes[ idx ].z - i_nodes[ idx-1 ].z; + len += sqrtf( xd*xd + yd*yd + zd*zd ); + } + + if(curnode > 0) + { + xd = x - i_nodes[curnode-1].x; + yd = y - i_nodes[curnode-1].y; + zd = z - i_nodes[curnode-1].z; + len += sqrtf( xd*xd + yd*yd + zd*zd ); + } + + return len; + } + + PathNode& operator[](const unsigned int idx) { return i_nodes[idx]; } + const PathNode& operator()(const unsigned int idx) const { return i_nodes[idx]; } + + protected: + std::vector i_nodes; +}; +#endif diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp new file mode 100644 index 00000000000..c80bbc8f673 --- /dev/null +++ b/src/game/Pet.cpp @@ -0,0 +1,1750 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 = 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/Pet.h b/src/game/Pet.h new file mode 100644 index 00000000000..f7866cbe0fc --- /dev/null +++ b/src/game/Pet.h @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_PET_H +#define MANGOSSERVER_PET_H + +#include "ObjectDefines.h" +#include "Creature.h" +#include "Unit.h" + +enum PetType +{ + SUMMON_PET = 0, + HUNTER_PET = 1, + GUARDIAN_PET = 2, + MINI_PET = 3, + MAX_PET_TYPE = 4 +}; + +extern char const* petTypeSuffix[MAX_PET_TYPE]; + +enum PetSaveMode +{ + PET_SAVE_AS_DELETED =-1, + PET_SAVE_AS_CURRENT = 0, + PET_SAVE_IN_STABLE_SLOT_1 = 1, + PET_SAVE_IN_STABLE_SLOT_2 = 2, + PET_SAVE_NOT_IN_SLOT = 3 +}; + +enum HappinessState +{ + UNHAPPY = 1, + CONTENT = 2, + HAPPY = 3 +}; + +enum LoyaltyLevel +{ + REBELLIOUS = 1, + UNRULY = 2, + SUBMISSIVE = 3, + DEPENDABLE = 4, + FAITHFUL = 5, + BEST_FRIEND = 6 +}; + +enum PetSpellState +{ + PETSPELL_UNCHANGED = 0, + PETSPELL_CHANGED = 1, + PETSPELL_NEW = 2, + PETSPELL_REMOVED = 3 +}; + +enum PetSpellType +{ + PETSPELL_NORMAL = 0, + PETSPELL_FAMILY = 1, +}; + +struct PetSpell +{ + uint16 slotId; + uint16 active; + PetSpellState state : 16; + PetSpellType type : 16; +}; + +enum ActionFeedback +{ + FEEDBACK_NONE = 0, + FEEDBACK_PET_DEAD = 1, + FEEDBACK_NOTHING_TO_ATT = 2, + FEEDBACK_CANT_ATT_TARGET = 3 +}; + +enum PetTalk +{ + PET_TALK_SPECIAL_SPELL = 0, + PET_TALK_ATTACK = 1 +}; + +typedef HM_NAMESPACE::hash_map PetSpellMap; +typedef std::map TeachSpellMap; +typedef std::vector AutoSpellList; + +#define HAPPINESS_LEVEL_SIZE 333000 + +extern const uint32 LevelUpLoyalty[6]; +extern const uint32 LevelStartLoyalty[6]; + +#define ACTIVE_SPELLS_MAX 4 + +#define OWNER_MAX_DISTANCE 100 + +#define PET_FOLLOW_DIST 1 +#define PET_FOLLOW_ANGLE (M_PI/2) + +class Pet : public Creature +{ + public: + explicit Pet(PetType type = MAX_PET_TYPE); + virtual ~Pet(); + + void AddToWorld(); + void RemoveFromWorld(); + + PetType getPetType() const { return m_petType; } + void setPetType(PetType type) { m_petType = type; } + bool isControlled() const { return getPetType()==SUMMON_PET || getPetType()==HUNTER_PET; } + bool isTemporarySummoned() const { return m_duration > 0; } + + bool Create (uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number); + bool CreateBaseAtCreature( Creature* creature ); + bool LoadPetFromDB( Unit* owner,uint32 petentry = 0,uint32 petnumber = 0, bool current = false ); + void SavePetToDB(PetSaveMode mode); + void Remove(PetSaveMode mode, bool returnreagent = false); + static void DeleteFromDB(uint32 guidlow); + + void setDeathState(DeathState s); // overwrite virtual Creature::setDeathState and Unit::setDeathState + void Update(uint32 diff); // overwrite virtual Creature::Update and Unit::Update + + uint8 GetPetAutoSpellSize() const { return m_autospells.size(); } + uint32 GetPetAutoSpellOnPos(uint8 pos) const + { + if (pos >= m_autospells.size()) + return 0; + else + return m_autospells[pos]; + } + + void RegenerateFocus(); + void LooseHappiness(); + void TickLoyaltyChange(); + void ModifyLoyalty(int32 addvalue); + HappinessState GetHappinessState(); + uint32 GetMaxLoyaltyPoints(uint32 level); + uint32 GetStartLoyaltyPoints(uint32 level); + void KillLoyaltyBonus(uint32 level); + uint32 GetLoyaltyLevel() { return GetByteValue(UNIT_FIELD_BYTES_1, 1); } + void SetLoyaltyLevel(LoyaltyLevel level); + void GivePetXP(uint32 xp); + void GivePetLevel(uint32 level); + bool InitStatsForLevel(uint32 level); + bool HaveInDiet(ItemPrototype const* item) const; + uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel); + void SetDuration(int32 dur) { m_duration = dur; } + + int32 GetBonusDamage() { return m_bonusdamage; } + void SetBonusDamage(int32 damage) { m_bonusdamage = damage; } + + 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); + + bool CanTakeMoreActiveSpells(uint32 SpellIconID); + void ToggleAutocast(uint32 spellid, bool apply); + bool HasTPForSpell(uint32 spellid); + int32 GetTPForSpell(uint32 spellid); + + bool HasSpell(uint32 spell) const; + void AddTeachSpell(uint32 learned_id, uint32 source_id) { m_teachspells[learned_id] = source_id; } + + void LearnPetPassives(); + void CastPetAuras(bool current); + void CastPetAura(PetAura const* aura); + + void _LoadSpellCooldowns(); + void _SaveSpellCooldowns(); + void _LoadAuras(uint32 timediff); + void _SaveAuras(); + void _LoadSpells(); + void _SaveSpells(); + + bool addSpell(uint16 spell_id,uint16 active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, uint16 slot_id=0xffff, PetSpellType type = PETSPELL_NORMAL); + bool learnSpell(uint16 spell_id); + void removeSpell(uint16 spell_id); + bool _removeSpell(uint16 spell_id); + + PetSpellMap m_spells; + TeachSpellMap m_teachspells; + AutoSpellList m_autospells; + + void InitPetCreateSpells(); + void CheckLearning(uint32 spellid); + uint32 resetTalentsCost() const; + + void SetTP(int32 TP); + int32 GetDispTP(); + + int32 m_TrainingPoints; + uint32 m_resetTalentsCost; + time_t m_resetTalentsTime; + + uint64 GetAuraUpdateMask() { return m_auraUpdateMask; } + void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); } + void ResetAuraUpdateMask() { m_auraUpdateMask = 0; } + + DeclinedName const* GetDeclinedNames() const { return m_declinedname; } + + bool m_removed; // prevent overwrite pet state in DB at next Pet::Update if pet already removed(saved) + protected: + uint32 m_regenTimer; + uint32 m_happinessTimer; + uint32 m_loyaltyTimer; + PetType m_petType; + int32 m_duration; // time until unsummon (used mostly for summoned guardians and not used for controlled pets) + int32 m_loyaltyPoints; + int32 m_bonusdamage; + uint64 m_auraUpdateMask; + + DeclinedName *m_declinedname; + + private: + void SaveToDB(uint32, uint8) // overwrited of Creature::SaveToDB - don't must be called + { + assert(false); + } + void DeleteFromDB() // overwrited of Creature::DeleteFromDB - don't must be called + { + assert(false); + } +}; +#endif diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp new file mode 100644 index 00000000000..c9aeaf66ec9 --- /dev/null +++ b/src/game/PetAI.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PetAI.h" +#include "Errors.h" +#include "Pet.h" +#include "Player.h" +#include "Database/DBCStores.h" +#include "Spell.h" +#include "ObjectAccessor.h" +#include "SpellMgr.h" +#include "Creature.h" +#include "World.h" +#include "Util.h" + +int PetAI::Permissible(const Creature *creature) +{ + if( creature->isPet()) + return PERMIT_BASE_SPECIAL; + + return PERMIT_BASE_NO; +} + +PetAI::PetAI(Creature &c) : i_pet(c), i_victimGuid(0), i_tracker(TIME_INTERVAL_LOOK) +{ + m_AllySet.clear(); + UpdateAllies(); +} + +void PetAI::MoveInLineOfSight(Unit *u) +{ + if( !i_pet.getVictim() && i_pet.GetCharmInfo() && + i_pet.GetCharmInfo()->HasReactState(REACT_AGGRESSIVE) && + u->isTargetableForAttack() && i_pet.IsHostileTo( u ) && + u->isInAccessablePlaceFor(&i_pet)) + { + float attackRadius = i_pet.GetAttackDistance(u); + if(i_pet.IsWithinDistInMap(u, attackRadius) && i_pet.GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE) + { + if(i_pet.IsWithinLOSInMap(u)) + { + AttackStart(u); + u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); + } + } + } +} + +void PetAI::AttackStart(Unit *u) +{ + if( i_pet.getVictim() || !u || i_pet.isPet() && ((Pet&)i_pet).getPetType()==MINI_PET ) + return; + + if(i_pet.Attack(u,true)) + { + i_pet.clearUnitState(UNIT_STAT_FOLLOW); + // TMGs call CreatureRelocation which via MoveInLineOfSight can call this function + // thus with the following clear the original TMG gets invalidated and crash, doh + // hope it doesn't start to leak memory without this :-/ + //i_pet->Clear(); + i_victimGuid = u->GetGUID(); + i_pet.GetMotionMaster()->MoveChase(u); + } +} + +void PetAI::EnterEvadeMode() +{ +} + +bool PetAI::IsVisible(Unit *pl) const +{ + return _isVisible(pl); +} + +bool PetAI::_needToStop() const +{ + if(!i_pet.getVictim() || !i_pet.isAlive()) + return true; + + // This is needed for charmed creatures, as once their target was reset other effects can trigger threat + if(i_pet.isCharmed() && i_pet.getVictim() == i_pet.GetCharmer()) + return true; + + return !i_pet.getVictim()->isTargetableForAttack(); +} + +void PetAI::_stopAttack() +{ + if( !i_victimGuid ) + return; + + Unit* victim = ObjectAccessor::GetUnit(i_pet, i_victimGuid ); + + if ( !victim ) + return; + + assert(!i_pet.getVictim() || i_pet.getVictim() == victim); + + if( !i_pet.isAlive() ) + { + DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_pet.GetGUIDLow()); + i_pet.StopMoving(); + i_pet.GetMotionMaster()->Clear(); + i_pet.GetMotionMaster()->MoveIdle(); + i_victimGuid = 0; + i_pet.CombatStop(); + i_pet.getHostilRefManager().deleteReferences(); + + return; + } + else if( !victim ) + { + DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_pet.GetGUIDLow()); + } + else if( !victim->isAlive() ) + { + DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_pet.GetGUIDLow()); + } + else if( victim->HasStealthAura() ) + { + DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_pet.GetGUIDLow()); + } + else if( victim->isInFlight() ) + { + DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_pet.GetGUIDLow()); + } + else + { + DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_pet.GetGUIDLow()); + } + + Unit* owner = i_pet.GetCharmerOrOwner(); + + if(owner && i_pet.GetCharmInfo() && i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) + { + i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + } + else + { + i_pet.clearUnitState(UNIT_STAT_FOLLOW); + i_pet.GetMotionMaster()->Clear(); + i_pet.GetMotionMaster()->MoveIdle(); + } + i_victimGuid = 0; + i_pet.AttackStop(); +} + +void PetAI::UpdateAI(const uint32 diff) +{ + // update i_victimGuid if i_pet.getVictim() !=0 and changed + if(i_pet.getVictim()) + i_victimGuid = i_pet.getVictim()->GetGUID(); + + Unit* owner = i_pet.GetCharmerOrOwner(); + + if(m_updateAlliesTimer <= diff) + // UpdateAllies self set update timer + UpdateAllies(); + else + m_updateAlliesTimer -= diff; + + // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc. + if( i_victimGuid ) + { + if( _needToStop() ) + { + DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow()); + _stopAttack(); // i_victimGuid == 0 && i_pet.getVictim() == NULL now + return; + } + else if( i_pet.IsStopped() || i_pet.IsWithinDistInMap(i_pet.getVictim(), ATTACK_DISTANCE)) + { + // required to be stopped cases + if ( i_pet.IsStopped() && i_pet.IsNonMeleeSpellCasted(false) ) + { + if( i_pet.hasUnitState(UNIT_STAT_FOLLOW) ) + i_pet.InterruptNonMeleeSpells(false); + else + return; + } + // not required to be stopped case + else if( i_pet.isAttackReady() && i_pet.canReachWithAttack(i_pet.getVictim()) ) + { + i_pet.AttackerStateUpdate(i_pet.getVictim()); + + i_pet.resetAttackTimer(); + + if ( !i_pet.getVictim() ) + return; + + //if pet misses its target, it will also be the first in threat list + i_pet.getVictim()->AddThreat(&i_pet,0.0f); + + if( _needToStop() ) + _stopAttack(); + } + } + } + else if(owner && i_pet.GetCharmInfo()) + { + if(owner->isInCombat() && !(i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) || i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY))) + { + AttackStart(owner->getAttackerForHelper()); + } + else if(i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) + { + if (!i_pet.hasUnitState(UNIT_STAT_FOLLOW) ) + { + i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + } + } + } + + //Autocast + HM_NAMESPACE::hash_map targetMap; + targetMap.clear(); + SpellCastTargets NULLtargets; + + for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++) + { + uint32 spellID = i_pet.GetPetAutoSpellOnPos(i); + if (!spellID) + continue; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID); + if (!spellInfo) + continue; + + Spell *spell = new Spell(&i_pet, spellInfo, false, 0); + + if(!IsPositiveSpell(spellInfo->Id) && i_pet.getVictim() && !_needToStop() && !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(i_pet.getVictim())) + targetMap[spellID] = i_pet.getVictim(); + else + { + spell->m_targets = NULLtargets; + for(std::set::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar) + { + Unit* Target = ObjectAccessor::GetUnit(i_pet,*tar); + + //only buff targets that are in combat, unless the spell can only be cast while out of combat + if(!Target || (!Target->isInCombat() && !IsNonCombatSpell(spellInfo))) + continue; + if(spell->CanAutoCast(Target)) + targetMap[spellID] = Target; + } + } + + delete spell; + } + + //found units to cast on to + if(!targetMap.empty()) + { + uint32 index = urand(1, targetMap.size()); + HM_NAMESPACE::hash_map::iterator itr; + uint32 i; + for(itr = targetMap.begin(), i = 1; i < index; ++itr, ++i); + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first); + + Spell *spell = new Spell(&i_pet, spellInfo, false); + + SpellCastTargets targets; + targets.setUnitTarget( itr->second ); + + if(!i_pet.HasInArc(M_PI, itr->second)) + { + i_pet.SetInFront(itr->second); + if( itr->second->GetTypeId() == TYPEID_PLAYER ) + i_pet.SendUpdateToPlayer( (Player*)itr->second ); + + if(owner && owner->GetTypeId() == TYPEID_PLAYER) + i_pet.SendUpdateToPlayer( (Player*)owner ); + } + + i_pet.AddCreatureSpellCooldown(itr->first); + if(i_pet.isPet()) + ((Pet*)&i_pet)->CheckLearning(itr->first); + + spell->prepare(&targets); + } + targetMap.clear(); +} + +bool PetAI::_isVisible(Unit *u) const +{ + //return false; //( ((Creature*)&i_pet)->GetDistanceSq(u) * 1.0<= sWorld.getConfig(CONFIG_SIGHT_GUARDER) && !u->m_stealth && u->isAlive()); + return i_pet.GetDistance(u) < sWorld.getConfig(CONFIG_SIGHT_GUARDER) + && u->isVisibleForOrDetect(&i_pet,true); +} + +void PetAI::UpdateAllies() +{ + Unit* owner = i_pet.GetCharmerOrOwner(); + Group *pGroup = NULL; + + m_updateAlliesTimer = 10000; //update friendly targets every 10 seconds, lesser checks increase performance + + if(!owner) + return; + else if(owner->GetTypeId() == TYPEID_PLAYER) + pGroup = ((Player*)owner)->GetGroup(); + + //only pet and owner/not in group->ok + if(m_AllySet.size() == 2 && !pGroup) + return; + //owner is in group; group members filled in already (no raid -> subgroupcount = whole count) + if(pGroup && !pGroup->isRaidGroup() && m_AllySet.size() == (pGroup->GetMembersCount() + 2)) + return; + + m_AllySet.clear(); + m_AllySet.insert(i_pet.GetGUID()); + if(pGroup) //add group + { + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* Target = itr->getSource(); + if(!Target || !pGroup->SameSubGroup((Player*)owner, Target)) + continue; + + if(Target->GetGUID() == owner->GetGUID()) + continue; + + m_AllySet.insert(Target->GetGUID()); + } + } + else //remove group + m_AllySet.insert(owner->GetGUID()); +} + +void PetAI::AttackedBy(Unit *attacker) +{ + //when attacked, fight back in case 1)no victim already AND 2)not set to passive AND 3)not set to stay, unless can it can reach attacker with melee attack anyway + if(!i_pet.getVictim() && i_pet.GetCharmInfo() && !i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) && + (!i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY) || i_pet.canReachWithAttack(attacker))) + AttackStart(attacker); +} diff --git a/src/game/PetAI.h b/src/game/PetAI.h new file mode 100644 index 00000000000..b8124bb3520 --- /dev/null +++ b/src/game/PetAI.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_PETAI_H +#define MANGOS_PETAI_H + +#include "CreatureAI.h" +#include "Timer.h" + +class Creature; + +class MANGOS_DLL_DECL PetAI : public CreatureAI +{ + public: + + PetAI(Creature &c); + + void MoveInLineOfSight(Unit *); + void AttackStart(Unit *); + void EnterEvadeMode(); + void DamageTaken(Unit *done_by, uint32& /*damage*/) { AttackedBy(done_by); } + void AttackedBy(Unit*); + bool IsVisible(Unit *) const; + + void UpdateAI(const uint32); + void UpdateAllies(); + static int Permissible(const Creature *); + + private: + bool _isVisible(Unit *) const; + bool _needToStop(void) const; + void _stopAttack(void); + + Creature &i_pet; + uint64 i_victimGuid; + TimeTracker i_tracker; + //uint32 i_RepeatAction; + std::set m_AllySet; + uint32 m_updateAlliesTimer; +}; +#endif diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp new file mode 100644 index 00000000000..3251d02d5b9 --- /dev/null +++ b/src/game/PetHandler.cpp @@ -0,0 +1,648 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "ObjectMgr.h" +#include "SpellMgr.h" +#include "Log.h" +#include "Opcodes.h" +#include "Spell.h" +#include "ObjectAccessor.h" +#include "MapManager.h" +#include "CreatureAI.h" +#include "Util.h" +#include "Pet.h" + +void WorldSession::HandlePetAction( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+2+2+8); + + uint64 guid1; + uint16 spellid; + uint16 flag; + uint64 guid2; + recv_data >> guid1; //pet guid + recv_data >> spellid; + recv_data >> flag; //delete = 0x0700 CastSpell = C100 + recv_data >> guid2; //tag guid + + // used also for charmed creature + Unit* pet= ObjectAccessor::GetUnit(*_player,guid1); + sLog.outDetail( "HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.\n", uint32(GUID_LOPART(guid1)), flag, spellid, uint32(GUID_LOPART(guid2)) ); + if(!pet) + { + sLog.outError( "Pet %u not exist.\n", uint32(GUID_LOPART(guid1)) ); + return; + } + + if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) + { + sLog.outError( "HandlePetAction.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid1)),GetPlayer()->GetName() ); + return; + } + + if(!pet->isAlive()) + return; + + if(pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK)) + return; + + CharmInfo *charmInfo = pet->GetCharmInfo(); + if(!charmInfo) + { + sLog.outError("WorldSession::HandlePetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); + return; + } + + switch(flag) + { + case ACT_COMMAND: //0x0700 + switch(spellid) + { + case COMMAND_STAY: //flat=1792 //STAY + pet->StopMoving(); + pet->GetMotionMaster()->Clear(); + pet->GetMotionMaster()->MoveIdle(); + charmInfo->SetCommandState( COMMAND_STAY ); + break; + case COMMAND_FOLLOW: //spellid=1792 //FOLLOW + pet->AttackStop(); + pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); + charmInfo->SetCommandState( COMMAND_FOLLOW ); + break; + case COMMAND_ATTACK: //spellid=1792 //ATTACK + { + // only place where pet can be player + pet->clearUnitState(UNIT_STAT_FOLLOW); + uint64 selguid = _player->GetSelection(); + Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, selguid); + if(!TargetUnit) + return; + + // not let attack friendly units. + if( GetPlayer()->IsFriendlyTo(TargetUnit)) + return; + + if(pet->getVictim()) + pet->AttackStop(); + + if(pet->GetTypeId() != TYPEID_PLAYER) + { + pet->GetMotionMaster()->Clear(); + if (((Creature*)pet)->AI()) + ((Creature*)pet)->AI()->AttackStart(TargetUnit); + + //10% chance to play special pet attack talk, else growl + if(((Creature*)pet)->isPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10) + pet->SendPetTalk((uint32)PET_TALK_ATTACK); + else + { + // 90% chance for pet and 100% chance for charmed creature + pet->SendPetAIReaction(guid1); + } + } + else // charmed player + { + pet->Attack(TargetUnit,true); + pet->SendPetAIReaction(guid1); + } + break; + } + case COMMAND_ABANDON: // abandon (hunter pet) or dismiss (summoned pet) + if(((Creature*)pet)->isPet()) + { + Pet* p = (Pet*)pet; + if(p->getPetType() == HUNTER_PET) + _player->RemovePet(p,PET_SAVE_AS_DELETED); + else + //dismissing a summoned pet is like killing them (this prevents returning a soulshard...) + p->setDeathState(CORPSE); + } + else // charmed + _player->Uncharm(); + break; + default: + sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); + } + break; + case ACT_REACTION: // 0x600 + switch(spellid) + { + case REACT_PASSIVE: //passive + case REACT_DEFENSIVE: //recovery + case REACT_AGGRESSIVE: //activete + charmInfo->SetReactState( ReactStates(spellid) ); + break; + } + break; + case ACT_DISABLED: //0x8100 spell (disabled), ignore + case ACT_CAST: //0x0100 + case ACT_ENABLED: //0xc100 spell + { + Unit* unit_target; + if(guid2) + unit_target = ObjectAccessor::GetUnit(*_player,guid2); + else + unit_target = NULL; + + // do not cast unknown spells + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid ); + if(!spellInfo) + { + sLog.outError("WORLD: unknown PET spell id %i\n", spellid); + return; + } + + for(uint32 i = 0; i < 3;i++) + { + if(spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || spellInfo->EffectImplicitTargetA[i] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED) + return; + } + + // do not cast not learned spells + if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) + return; + + pet->clearUnitState(UNIT_STAT_FOLLOW); + + Spell *spell = new Spell(pet, spellInfo, false); + + int16 result = spell->PetCanCast(unit_target); + + //auto turn to target unless possessed + if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) + { + pet->SetInFront(unit_target); + if( unit_target->GetTypeId() == TYPEID_PLAYER ) + pet->SendUpdateToPlayer( (Player*)unit_target ); + if(Unit* powner = pet->GetCharmerOrOwner()) + if(powner->GetTypeId() == TYPEID_PLAYER) + pet->SendUpdateToPlayer((Player*)powner); + result = -1; + } + + if(result == -1) + { + ((Creature*)pet)->AddCreatureSpellCooldown(spellid); + if (((Creature*)pet)->isPet()) + ((Pet*)pet)->CheckLearning(spellid); + + unit_target = spell->m_targets.getUnitTarget(); + + //10% chance to play special pet attack talk, else growl + //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell + if(((Creature*)pet)->isPet() && (((Pet*)pet)->getPetType() == SUMMON_PET) && (pet != unit_target) && (urand(0, 100) < 10)) + pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); + else + { + pet->SendPetAIReaction(guid1); + } + + if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) + { + pet->clearUnitState(UNIT_STAT_FOLLOW); + if(pet->getVictim()) + pet->AttackStop(); + pet->GetMotionMaster()->Clear(); + if (((Creature*)pet)->AI()) + ((Creature*)pet)->AI()->AttackStart(unit_target); + } + + spell->prepare(&(spell->m_targets)); + } + else + { + if(pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) + { + WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); + data << uint32(spellid) << uint8(2) << uint8(result); + switch (result) + { + case SPELL_FAILED_REQUIRES_SPELL_FOCUS: + data << uint32(spellInfo->RequiresSpellFocus); + break; + case SPELL_FAILED_REQUIRES_AREA: + data << uint32(spellInfo->AreaId); + break; + } + SendPacket(&data); + } + else + pet->SendPetCastFail(spellid, result); + + if(!((Creature*)pet)->HasSpellCooldown(spellid)) + pet->SendPetClearCooldown(spellid); + + spell->finish(false); + delete spell; + } + break; + } + default: + sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.\n", flag, spellid); + } +} + +void WorldSession::HandlePetNameQuery( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+8); + + sLog.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY\n" ); + + uint32 petnumber; + uint64 petguid; + + recv_data >> petnumber; + recv_data >> petguid; + + SendPetNameQuery(petguid,petnumber); +} + +void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber) +{ + Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid); + if(!pet || !pet->GetCharmInfo() || pet->GetCharmInfo()->GetPetNumber() != petnumber) + return; + + std::string name = pet->GetName(); + + WorldPacket data(SMSG_PET_NAME_QUERY_RESPONSE, (4+4+name.size()+1)); + data << uint32(petnumber); + data << name.c_str(); + data << uint32(pet->GetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP)); + + if( pet->isPet() && ((Pet*)pet)->GetDeclinedNames() ) + { + data << uint8(1); + for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + data << ((Pet*)pet)->GetDeclinedNames()->name[i]; + } + else + data << uint8(0); + + _player->GetSession()->SendPacket(&data); +} + +void WorldSession::HandlePetSetAction( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4+2+2); + + sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION\n" ); + + uint64 petguid; + uint32 position; + uint16 spell_id; + uint16 act_state; + uint8 count; + + recv_data >> petguid; + + // FIXME: charmed case + //Pet* pet = ObjectAccessor::Instance().GetPet(petguid); + if(ObjectAccessor::FindPlayer(petguid)) + return; + + Creature* pet = ObjectAccessor::GetCreatureOrPet(*_player, petguid); + + if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) + { + sLog.outError( "HandlePetSetAction: Unknown pet or pet owner.\n" ); + return; + } + + CharmInfo *charmInfo = pet->GetCharmInfo(); + if(!charmInfo) + { + sLog.outError("WorldSession::HandlePetSetAction: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); + return; + } + + count = (recv_data.size() == 24) ? 2 : 1; + for(uint8 i = 0; i < count; i++) + { + recv_data >> position; + recv_data >> spell_id; + recv_data >> act_state; + + sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X\n", _player->GetName(), position, spell_id, act_state); + + //if it's act for spell (en/disable/cast) and there is a spell given (0 = remove spell) which pet doesn't know, don't add + if(!((act_state == ACT_ENABLED || act_state == ACT_DISABLED || act_state == ACT_CAST) && spell_id && !pet->HasSpell(spell_id))) + { + //sign for autocast + if(act_state == ACT_ENABLED && spell_id) + { + if(pet->isCharmed()) + charmInfo->ToggleCreatureAutocast(spell_id, true); + else + ((Pet*)pet)->ToggleAutocast(spell_id, true); + } + //sign for no/turn off autocast + else if(act_state == ACT_DISABLED && spell_id) + { + if(pet->isCharmed()) + charmInfo->ToggleCreatureAutocast(spell_id, false); + else + ((Pet*)pet)->ToggleAutocast(spell_id, false); + } + + charmInfo->GetActionBarEntry(position)->Type = act_state; + charmInfo->GetActionBarEntry(position)->SpellOrAction = spell_id; + } + } +} + +void WorldSession::HandlePetRename( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+1+1+1+1+1+1+1); + + sLog.outDetail( "HandlePetRename. CMSG_PET_RENAME\n" ); + + uint64 petguid; + uint8 isdeclined; + + std::string name; + DeclinedName declinedname; + + recv_data >> petguid; + recv_data >> name; + recv_data >> isdeclined; + + Pet* pet = ObjectAccessor::GetPet(petguid); + // check it! + if( !pet || !pet->isPet() || ((Pet*)pet)->getPetType()!= HUNTER_PET || + pet->GetByteValue(UNIT_FIELD_BYTES_2, 2) != UNIT_RENAME_ALLOWED || + pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo() ) + return; + + if((!ObjectMgr::IsValidPetName(name)) || (objmgr.IsReservedName(name))) + { + SendNotification("Invalid name"); + return; + } + pet->SetName(name); + + Unit *owner = pet->GetOwner(); + if(owner && (owner->GetTypeId() == TYPEID_PLAYER) && ((Player*)owner)->GetGroup()) + ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_NAME); + + pet->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED); + + if(isdeclined) + { + for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + recv_data >> declinedname.name[i]; + + std::wstring wname; + Utf8toWStr(name,wname); + if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname,0),declinedname)) + { + SendNotification("Invalid name"); + return; + } + } + + CharacterDatabase.BeginTransaction(); + if(isdeclined) + { + for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + CharacterDatabase.escape_string(declinedname.name[i]); + CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'", _player->GetGUIDLow(), pet->GetCharmInfo()->GetPetNumber()); + CharacterDatabase.PExecute("INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES ('%u','%u','%s','%s','%s','%s','%s')", + pet->GetCharmInfo()->GetPetNumber(), _player->GetGUIDLow(), declinedname.name[0].c_str(),declinedname.name[1].c_str(),declinedname.name[2].c_str(),declinedname.name[3].c_str(),declinedname.name[4].c_str()); + } + + CharacterDatabase.escape_string(name); + CharacterDatabase.PExecute("UPDATE character_pet SET name = '%s', renamed = '1' WHERE owner = '%u' AND id = '%u'", name.c_str(),_player->GetGUIDLow(),pet->GetCharmInfo()->GetPetNumber() ); + CharacterDatabase.CommitTransaction(); + + pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); +} + +void WorldSession::HandlePetAbandon( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid; + recv_data >> guid; //pet guid + sLog.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid) ); + + // pet/charmed + Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player, guid); + if(pet) + { + if(pet->isPet()) + { + if(pet->GetGUID() == _player->GetPetGUID()) + { + uint32 feelty = pet->GetPower(POWER_HAPPINESS); + pet->SetPower(POWER_HAPPINESS ,(feelty-50000) > 0 ?(feelty-50000) : 0); + } + + _player->RemovePet((Pet*)pet,PET_SAVE_AS_DELETED); + } + else if(pet->GetGUID() == _player->GetCharmGUID()) + { + _player->Uncharm(); + } + } +} + +void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,8); + + sLog.outDetail("CMSG_PET_UNLEARN"); + uint64 guid; + recvPacket >> guid; + + Pet* pet = _player->GetPet(); + + if(!pet || pet->getPetType() != HUNTER_PET || pet->m_spells.size() <= 1) + return; + + if(guid != pet->GetGUID()) + { + sLog.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); + return; + } + + CharmInfo *charmInfo = pet->GetCharmInfo(); + if(!charmInfo) + { + sLog.outError("WorldSession::HandlePetUnlearnOpcode: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); + return; + } + + uint32 cost = pet->resetTalentsCost(); + + if (GetPlayer()->GetMoney() < cost) + { + GetPlayer()->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0); + return; + } + + for(PetSpellMap::iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end();) + { + uint32 spell_id = itr->first; // Pet::removeSpell can invalidate iterator at erase NEW spell + ++itr; + pet->removeSpell(spell_id); + } + + pet->SetTP(pet->getLevel() * (pet->GetLoyaltyLevel() - 1)); + + for(uint8 i = 0; i < 10; i++) + { + if(charmInfo->GetActionBarEntry(i)->SpellOrAction && charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) + charmInfo->GetActionBarEntry(i)->SpellOrAction = 0; + } + + // relearn pet passives + pet->LearnPetPassives(); + + pet->m_resetTalentsTime = time(NULL); + pet->m_resetTalentsCost = cost; + GetPlayer()->ModifyMoney(-(int32)cost); + + GetPlayer()->PetSpellInitialize(); +} + +void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket ) +{ + CHECK_PACKET_SIZE(recvPacket,8+2+2+1); + + sLog.outDetail("CMSG_PET_SPELL_AUTOCAST"); + uint64 guid; + uint16 spellid; + uint16 spellid2; //maybe second spell, automatically toggled off when first toggled on? + uint8 state; //1 for on, 0 for off + recvPacket >> guid >> spellid >> spellid2 >> state; + + if(!_player->GetPet() && !_player->GetCharm()) + return; + + if(ObjectAccessor::FindPlayer(guid)) + return; + + Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid); + + if(!pet || (pet != _player->GetPet() && pet != _player->GetCharm())) + { + sLog.outError( "HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); + return; + } + + // do not add not learned spells/ passive spells + if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) + return; + + CharmInfo *charmInfo = pet->GetCharmInfo(); + if(!charmInfo) + { + sLog.outError("WorldSession::HandlePetSpellAutocastOpcod: object "I64FMTD" is considered pet-like but doesn't have a charminfo!", pet->GetGUID()); + return; + } + + if(pet->isCharmed()) + //state can be used as boolean + pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state); + else + ((Pet*)pet)->ToggleAutocast(spellid, state); + + for(uint8 i = 0; i < 10; ++i) + { + if((charmInfo->GetActionBarEntry(i)->Type == ACT_ENABLED || charmInfo->GetActionBarEntry(i)->Type == ACT_DISABLED) && spellid == charmInfo->GetActionBarEntry(i)->SpellOrAction) + charmInfo->GetActionBarEntry(i)->Type = state ? ACT_ENABLED : ACT_DISABLED; + } +} + +void WorldSession::HandleAddDynamicTargetObsoleteOpcode( WorldPacket& recvPacket ) +{ + sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL"); + + CHECK_PACKET_SIZE(recvPacket,8+4); + uint64 guid; + uint32 spellid; + + recvPacket >> guid >> spellid; + + if(!_player->GetPet() && !_player->GetCharm()) + return; + + if(ObjectAccessor::FindPlayer(guid)) + return; + + Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid); + + if(!pet || (pet != _player->GetPet() && pet!= _player->GetCharm())) + { + sLog.outError( "HandleAddDynamicTargetObsoleteOpcode.Pet %u isn't pet of player %s .\n", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); + return; + } + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid); + if(!spellInfo) + { + sLog.outError("WORLD: unknown PET spell id %i\n", spellid); + return; + } + + // do not cast not learned spells + if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid)) + return; + + SpellCastTargets targets; + if(!targets.read(&recvPacket,pet)) + return; + + pet->clearUnitState(UNIT_STAT_FOLLOW); + + Spell *spell = new Spell(pet, spellInfo, false); + spell->m_targets = targets; + + int16 result = spell->PetCanCast(NULL); + if(result == -1) + { + pet->AddCreatureSpellCooldown(spellid); + if(pet->isPet()) + { + Pet* p = (Pet*)pet; + p->CheckLearning(spellid); + //10% chance to play special pet attack talk, else growl + //actually this only seems to happen on special spells, fire shield for imp, torment for voidwalker, but it's stupid to check every spell + if(p->getPetType() == SUMMON_PET && (urand(0, 100) < 10)) + pet->SendPetTalk((uint32)PET_TALK_SPECIAL_SPELL); + else + pet->SendPetAIReaction(guid); + } + + spell->prepare(&(spell->m_targets)); + } + else + { + pet->SendPetCastFail(spellid, result); + if(!pet->HasSpellCooldown(spellid)) + pet->SendPetClearCooldown(spellid); + + spell->finish(false); + delete spell; + } +} diff --git a/src/game/PetitionsHandler.cpp b/src/game/PetitionsHandler.cpp new file mode 100644 index 00000000000..6957634dc9e --- /dev/null +++ b/src/game/PetitionsHandler.cpp @@ -0,0 +1,936 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Language.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "ObjectMgr.h" +#include "Log.h" +#include "Opcodes.h" +#include "Guild.h" +#include "ArenaTeam.h" +#include "MapManager.h" +#include "GossipDef.h" +#include "SocialMgr.h" + +/*enum PetitionType // dbc data +{ + PETITION_TYPE_GUILD = 1, + PETITION_TYPE_ARENA_TEAM = 3 +};*/ + +// Charters ID in item_template +#define GUILD_CHARTER 5863 +#define GUILD_CHARTER_COST 1000 // 10 S +#define ARENA_TEAM_CHARTER_2v2 23560 +#define ARENA_TEAM_CHARTER_2v2_COST 800000 // 80 G +#define ARENA_TEAM_CHARTER_3v3 23561 +#define ARENA_TEAM_CHARTER_3v3_COST 1200000 // 120 G +#define ARENA_TEAM_CHARTER_5v5 23562 +#define ARENA_TEAM_CHARTER_5v5_COST 2000000 // 200 G + +void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 8+8+4+1+5*8+2+1+4+4); + + sLog.outDebug("Received opcode CMSG_PETITION_BUY"); + //recv_data.hexlike(); + + uint64 guidNPC; + uint64 unk1, unk3, unk4, unk5, unk6, unk7; + uint32 unk2; + std::string name; + uint16 unk8; + uint8 unk9; + uint32 unk10; // selected index + uint32 unk11; + recv_data >> guidNPC; // NPC GUID + recv_data >> unk1; // 0 + recv_data >> unk2; // 0 + recv_data >> name; // name + + // recheck + CHECK_PACKET_SIZE(recv_data, 8+8+4+(name.size()+1)+5*8+2+1+4+4); + + recv_data >> unk3; // 0 + recv_data >> unk4; // 0 + recv_data >> unk5; // 0 + recv_data >> unk6; // 0 + recv_data >> unk7; // 0 + recv_data >> unk8; // 0 + recv_data >> unk9; // 0 + recv_data >> unk10; // index + recv_data >> unk11; // 0 + sLog.outDebug("Petitioner with GUID %u tried sell petition: name %s", GUID_LOPART(guidNPC), name.c_str()); + + // prevent cheating + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guidNPC,UNIT_NPC_FLAG_PETITIONER); + if (!pCreature) + { + sLog.outDebug("WORLD: HandlePetitionBuyOpcode - Unit (GUID: %u) not found or you can't interact with him.", GUID_LOPART(guidNPC)); + return; + } + + // remove fake death + if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + + uint32 charterid = 0; + uint32 cost = 0; + uint32 type = 0; + if(pCreature->isTabardDesigner()) + { + // if tabard designer, then trying to buy a guild charter. + // do not let if already in guild. + if(_player->GetGuildId()) + return; + + charterid = GUILD_CHARTER; + cost = GUILD_CHARTER_COST; + type = 9; + } + else + { + // TODO: find correct opcode + if(_player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + { + SendNotification(GetMangosString(LANG_ARENA_ONE_TOOLOW), 70); + return; + } + + for(uint8 i = 0; i < MAX_ARENA_SLOT; i++) + { + if(_player->GetArenaTeamId(i) && (i == (unk10-1))) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM); + return; + } + } + switch(unk10) + { + case 1: + charterid = ARENA_TEAM_CHARTER_2v2; + cost = ARENA_TEAM_CHARTER_2v2_COST; + type = 2; // 2v2 + break; + case 2: + charterid = ARENA_TEAM_CHARTER_3v3; + cost = ARENA_TEAM_CHARTER_3v3_COST; + type = 3; // 3v3 + break; + case 3: + charterid = ARENA_TEAM_CHARTER_5v5; + cost = ARENA_TEAM_CHARTER_5v5_COST; + type = 5; // 5v5 + break; + default: + sLog.outDebug("unknown selection at buy petition: %u", unk10); + return; + } + } + + if(type == 9) + { + if(objmgr.GetGuildByName(name)) + { + SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_EXISTS); + return; + } + if(objmgr.IsReservedName(name)) + { + SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_INVALID); + return; + } + if(!ObjectMgr::IsValidCharterName(name)) + { + SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_INVALID); + return; + } + } + else + { + if(objmgr.GetArenaTeamByName(name)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S); + return; + } + if(objmgr.IsReservedName(name)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID); + return; + } + if(!ObjectMgr::IsValidCharterName(name)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_INVALID); + return; + } + } + + ItemPrototype const *pProto = objmgr.GetItemPrototype(charterid); + if(!pProto) + { + _player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, NULL, charterid, 0); + return; + } + + if(_player->GetMoney() < cost) + { //player hasn't got enough money + _player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, charterid, 0); + return; + } + + ItemPosCountVec dest; + uint8 msg = _player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, charterid, pProto->BuyCount ); + if(msg != EQUIP_ERR_OK) + { + _player->SendBuyError(msg, pCreature, charterid, 0); + return; + } + + _player->ModifyMoney(-(int32)cost); + Item *charter = _player->StoreNewItem(dest, charterid, true); + if(!charter) + return; + + charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT, charter->GetGUIDLow()); + // ITEM_FIELD_ENCHANTMENT is guild/arenateam id + // ITEM_FIELD_ENCHANTMENT+1 is current signatures count (showed on item) + charter->SetState(ITEM_CHANGED, _player); + _player->SendNewItem(charter, 1, true, false); + + // a petition is invalid, if both the owner and the type matches + QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE ownerguid = '%u' AND type = '%u'", _player->GetGUIDLow(), type); + + std::ostringstream ssInvalidPetitionGUIDs; + + if (result) + { + + do + { + Field *fields = result->Fetch(); + ssInvalidPetitionGUIDs << "'" << fields[0].GetUInt32() << "' , "; + } while (result->NextRow()); + + delete result; + } + + // delete petitions with the same guid as this one + ssInvalidPetitionGUIDs << "'" << charter->GetGUIDLow() << "'"; + + sLog.outDebug("Invalid petition GUIDs: %s", ssInvalidPetitionGUIDs.str().c_str()); + CharacterDatabase.escape_string(name); + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid IN ( %s )", ssInvalidPetitionGUIDs.str().c_str()); + CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid IN ( %s )", ssInvalidPetitionGUIDs.str().c_str()); + CharacterDatabase.PExecute("INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES ('%u', '%u', '%s', '%u')", + _player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type); + CharacterDatabase.CommitTransaction(); +} + +void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + // ok + sLog.outDebug("Received opcode CMSG_PETITION_SHOW_SIGNATURES"); + //recv_data.hexlike(); + + uint8 signs = 0; + uint64 petitionguid; + recv_data >> petitionguid; // petition guid + + // solve (possible) some strange compile problems with explicit use GUID_LOPART(petitionguid) at some GCC versions (wrong code optimization in compiler?) + uint32 petitionguid_low = GUID_LOPART(petitionguid); + + QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid, type FROM petition WHERE petitionguid = '%u'", petitionguid_low); + if(!result) + { + sLog.outError("any petition on server..."); + return; + } + Field *fields = result->Fetch(); + uint32 type = fields[1].GetUInt32(); + delete result; + // if guild petition and has guild => error, return; + if(type==9 && _player->GetGuildId()) + return; + + result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", petitionguid_low); + + // result==NULL also correct in case no sign yet + if(result) + signs = result->GetRowCount(); + + sLog.outDebug("CMSG_PETITION_SHOW_SIGNATURES petition entry: '%u'", petitionguid_low); + + WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+1+signs*12)); + data << petitionguid; // petition guid + data << _player->GetGUID(); // owner guid + data << petitionguid_low; // guild guid (in mangos always same as GUID_LOPART(petitionguid) + data << signs; // sign's count + + for(uint8 i = 1; i <= signs; i++) + { + Field *fields = result->Fetch(); + uint64 plguid = fields[0].GetUInt64(); + + data << plguid; // Player GUID + data << (uint32)0; // there 0 ... + + result->NextRow(); + } + delete result; + SendPacket(&data); +} + +void WorldSession::HandlePetitionQueryOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 4+8); + + sLog.outDebug("Received opcode CMSG_PETITION_QUERY"); // ok + //recv_data.hexlike(); + + uint32 guildguid; + uint64 petitionguid; + recv_data >> guildguid; // in mangos always same as GUID_LOPART(petitionguid) + recv_data >> petitionguid; // petition guid + sLog.outDebug("CMSG_PETITION_QUERY Petition GUID %u Guild GUID %u", GUID_LOPART(petitionguid), guildguid); + + SendPetitionQueryOpcode(petitionguid); +} + +void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid) +{ + uint64 ownerguid = 0; + uint32 type; + std::string name = "NO_NAME_FOR_GUID"; + uint8 signs = 0; + + QueryResult *result = CharacterDatabase.PQuery( + "SELECT ownerguid, name, " + " (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs " + "FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid)); + + if(result) + { + Field* fields = result->Fetch(); + ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); + name = fields[1].GetCppString(); + signs = fields[2].GetUInt8(); + delete result; + } + else + { + sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid)); + return; + } + + QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + + if(result2) + { + Field* fields = result2->Fetch(); + type = fields[0].GetUInt32(); + delete result2; + } + else + { + sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid)); + return; + } + + WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4+8+name.size()+1+1+4*13)); + data << GUID_LOPART(petitionguid); // guild/team guid (in mangos always same as GUID_LOPART(petition guid) + data << ownerguid; // charter owner guid + data << name; // name (guild/arena team) + data << uint8(0); // 1 + if(type == 9) + { + data << uint32(9); + data << uint32(9); + data << uint32(0); // bypass client - side limitation, a different value is needed here for each petition + } + else + { + data << type-1; + data << type-1; + data << type; // bypass client - side limitation, a different value is needed here for each petition + } + data << uint32(0); // 5 + data << uint32(0); // 6 + data << uint32(0); // 7 + data << uint32(0); // 8 + data << uint16(0); // 9 2 bytes field + data << uint32(0); // 10 + data << uint32(0); // 11 + data << uint32(0); // 13 count of next strings? + data << uint32(0); // 14 + if(type == 9) + data << uint32(0); // 15 0 - guild, 1 - arena team + else + data << uint32(1); + SendPacket(&data); +} + +void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 8+1); + + sLog.outDebug("Received opcode MSG_PETITION_RENAME"); // ok + //recv_data.hexlike(); + + uint64 petitionguid; + uint32 type; + std::string newname; + + recv_data >> petitionguid; // guid + recv_data >> newname; // new name + + Item *item = _player->GetItemByGuid(petitionguid); + if(!item) + return; + + QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + + if(result2) + { + Field* fields = result2->Fetch(); + type = fields[0].GetUInt32(); + delete result2; + } + else + { + sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid)); + return; + } + + if(type == 9) + { + if(objmgr.GetGuildByName(newname)) + { + SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_EXISTS); + return; + } + if(objmgr.IsReservedName(newname)) + { + SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_INVALID); + return; + } + if(!ObjectMgr::IsValidCharterName(newname)) + { + SendGuildCommandResult(GUILD_CREATE_S, newname, GUILD_NAME_INVALID); + return; + } + } + else + { + if(objmgr.GetArenaTeamByName(newname)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_EXISTS_S); + return; + } + if(objmgr.IsReservedName(newname)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID); + return; + } + if(!ObjectMgr::IsValidCharterName(newname)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, newname, "", ERR_ARENA_TEAM_NAME_INVALID); + return; + } + } + + std::string db_newname = newname; + CharacterDatabase.escape_string(db_newname); + CharacterDatabase.PExecute("UPDATE petition SET name = '%s' WHERE petitionguid = '%u'", + db_newname.c_str(), GUID_LOPART(petitionguid)); + + sLog.outDebug("Petition (GUID: %u) renamed to '%s'", GUID_LOPART(petitionguid), newname.c_str()); + WorldPacket data(MSG_PETITION_RENAME, (8+newname.size()+1)); + data << petitionguid; + data << newname; + SendPacket(&data); +} + +void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 8+1); + + sLog.outDebug("Received opcode CMSG_PETITION_SIGN"); // ok + //recv_data.hexlike(); + + Field *fields; + uint64 petitionguid; + uint32 type; + uint8 unk; + uint64 ownerguid; + recv_data >> petitionguid; // petition guid + recv_data >> unk; + + uint8 signs = 0; + + QueryResult *result = CharacterDatabase.PQuery( + "SELECT ownerguid, " + " (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs " + "FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid)); + + if(!result) + { + sLog.outError("any petition on server..."); + return; + } + + fields = result->Fetch(); + ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); + signs = fields[1].GetUInt8(); + + delete result; + + uint32 plguidlo = _player->GetGUIDLow(); + if(GUID_LOPART(ownerguid) == plguidlo) + return; + + // not let enemies sign guild charter + if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != objmgr.GetPlayerTeamByGUID(ownerguid)) + return; + + QueryResult *result2 = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + + if(result2) + { + Field* fields = result2->Fetch(); + type = fields[0].GetUInt32(); + delete result2; + } + else + { + sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid)); + return; + } + + if(type != 9 && _player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) + { + // player is too low level to join an arena team + SendNotification("You must be level %u to join an arena team!",sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)); + return; + } + + signs += 1; + if(signs > type) // client signs maximum + return; + + //client doesn't allow to sign petition two times by one character, but not check sign by another character from same account + //not allow sign another player from already sign player account + result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE player_account = '%u' AND petitionguid = '%u'", GetAccountId(), GUID_LOPART(petitionguid)); + + if(result) + { + delete result; + WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4)); + data << petitionguid; + data << _player->GetGUID(); + data << (uint32)PETITION_SIGN_ALREADY_SIGNED; + + // close at signer side + SendPacket(&data); + + // update for owner if online + if(Player *owner = objmgr.GetPlayer(ownerguid)) + owner->GetSession()->SendPacket(&data); + return; + } + + CharacterDatabase.PExecute("INSERT INTO petition_sign (ownerguid,petitionguid, playerguid, player_account) VALUES ('%u', '%u', '%u','%u')", GUID_LOPART(ownerguid),GUID_LOPART(petitionguid), plguidlo,GetAccountId()); + + sLog.outDebug("PETITION SIGN: GUID %u by player: %s (GUID: %u Account: %u)", GUID_LOPART(petitionguid), _player->GetName(),plguidlo,GetAccountId()); + + WorldPacket data(SMSG_PETITION_SIGN_RESULTS, (8+8+4)); + data << petitionguid; + data << _player->GetGUID(); + data << (uint32)PETITION_SIGN_OK; + + // close at signer side + SendPacket(&data); + + // update signs count on charter, required testing... + //Item *item = _player->GetItemByGuid(petitionguid)); + //if(item) + // item->SetUInt32Value(ITEM_FIELD_ENCHANTMENT+1, signs); + + // update for owner if online + if(Player *owner = objmgr.GetPlayer(ownerguid)) + owner->GetSession()->SendPacket(&data); +} + +void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + sLog.outDebug("Received opcode MSG_PETITION_DECLINE"); // ok + //recv_data.hexlike(); + + uint64 petitionguid; + uint64 ownerguid; + recv_data >> petitionguid; // petition guid + sLog.outDebug("Petition %u declined by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow()); + + QueryResult *result = CharacterDatabase.PQuery("SELECT ownerguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + if(!result) + return; + + Field *fields = result->Fetch(); + ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER); + delete result; + + Player *owner = objmgr.GetPlayer(ownerguid); + if(owner) // petition owner online + { + WorldPacket data(MSG_PETITION_DECLINE, 8); + data << _player->GetGUID(); + owner->GetSession()->SendPacket(&data); + } +} + +void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 4+8+8); + + sLog.outDebug("Received opcode CMSG_OFFER_PETITION"); // ok + //recv_data.hexlike(); + + uint8 signs = 0; + uint64 petitionguid, plguid; + uint32 petitiontype; + Player *player; + recv_data >> petitiontype; // 2.0.8 - petition type? + recv_data >> petitionguid; // petition guid + recv_data >> plguid; // player guid + sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", petitiontype, GUID_LOPART(petitionguid), GUID_LOPART(plguid)); + + player = ObjectAccessor::FindPlayer(plguid); + if(!player || player->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) + return; + + // not let offer to enemies + if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam() ) + return; + + QueryResult *result = CharacterDatabase.PQuery("SELECT petitionguid FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + if(!result) + { + sLog.outError("any petition on server..."); + return; + } + + delete result; + + result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + // result==NULL also correct charter without signs + if(result) + signs = result->GetRowCount(); + + WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8+8+4+signs+signs*12)); + data << petitionguid; // petition guid + data << _player->GetGUID(); // owner guid + data << GUID_LOPART(petitionguid); // guild guid (in mangos always same as GUID_LOPART(petition guid) + data << signs; // sign's count + + for(uint8 i = 1; i <= signs; i++) + { + Field *fields = result->Fetch(); + uint64 plguid = fields[0].GetUInt64(); + + data << plguid; // Player GUID + data << (uint32)0; // there 0 ... + + result->NextRow(); + } + + delete result; + player->GetSession()->SendPacket(&data); +} + +void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + sLog.outDebug("Received opcode CMSG_TURN_IN_PETITION"); // ok + //recv_data.hexlike(); + + WorldPacket data; + uint64 petitionguid; + + uint32 ownerguidlo; + uint32 type; + std::string name; + + recv_data >> petitionguid; + + sLog.outDebug("Petition %u turned in by %u", GUID_LOPART(petitionguid), _player->GetGUIDLow()); + + // data + QueryResult *result = CharacterDatabase.PQuery("SELECT ownerguid, name, type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + if(result) + { + Field *fields = result->Fetch(); + ownerguidlo = fields[0].GetUInt32(); + name = fields[1].GetCppString(); + type = fields[2].GetUInt32(); + delete result; + } + else + { + sLog.outError("petition table has broken data!"); + return; + } + + if(type == 9) + { + if(_player->GetGuildId()) + { + data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4); + data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild + _player->GetSession()->SendPacket(&data); + return; + } + } + else + { + uint8 slot = ArenaTeam::GetSlotByType(type); + if(slot >= MAX_ARENA_SLOT) + return; + + if(_player->GetArenaTeamId(slot)) + { + //data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4); + //data << (uint32)PETITION_TURN_ALREADY_IN_GUILD; // already in guild + //_player->GetSession()->SendPacket(&data); + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM); + return; + } + } + + if(_player->GetGUIDLow() != ownerguidlo) + return; + + // signs + uint8 signs; + result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + if(result) + signs = result->GetRowCount(); + else + signs = 0; + + uint32 count; + //if(signs < sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS)) + if(type == 9) + count = sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS); + else + count = type-1; + if(signs < count) + { + data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4); + data << (uint32)PETITION_TURN_NEED_MORE_SIGNATURES; // need more signatures... + SendPacket(&data); + delete result; + return; + } + + if(type == 9) + { + if(objmgr.GetGuildByName(name)) + { + SendGuildCommandResult(GUILD_CREATE_S, name, GUILD_NAME_EXISTS); + delete result; + return; + } + } + else + { + if(objmgr.GetArenaTeamByName(name)) + { + SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ARENA_TEAM_NAME_EXISTS_S); + delete result; + return; + } + } + + // and at last charter item check + Item *item = _player->GetItemByGuid(petitionguid); + if(!item) + { + delete result; + return; + } + + // OK! + + // delete charter item + _player->DestroyItem(item->GetBagSlot(),item->GetSlot(), true); + + if(type == 9) // create guild + { + Guild* guild = new Guild; + if(!guild->create(_player->GetGUID(), name)) + { + delete guild; + delete result; + return; + } + + // register guild and add guildmaster + objmgr.AddGuild(guild); + + // add members + for(uint8 i = 0; i < signs; ++i) + { + Field* fields = result->Fetch(); + guild->AddMember(fields[0].GetUInt64(), guild->GetLowestRank()); + result->NextRow(); + } + } + else // or arena team + { + ArenaTeam* at = new ArenaTeam; + if(!at->create(_player->GetGUID(), type, name)) + { + sLog.outError("PetitionsHandler: arena team create failed."); + delete at; + delete result; + return; + } + + CHECK_PACKET_SIZE(recv_data, 8+5*4); + uint32 icon, iconcolor, border, bordercolor, backgroud; + recv_data >> backgroud >> icon >> iconcolor >> border >> bordercolor; + + at->SetEmblem(backgroud, icon, iconcolor, border, bordercolor); + + // register team and add captain + objmgr.AddArenaTeam(at); + sLog.outDebug("PetitonsHandler: arena team added to objmrg"); + + // add members + for(uint8 i = 0; i < signs; ++i) + { + Field* fields = result->Fetch(); + sLog.outDebug("PetitionsHandler: adding arena member %u", fields[0].GetUInt64()); + at->AddMember(fields[0].GetUInt64()); + result->NextRow(); + } + } + + delete result; + + CharacterDatabase.BeginTransaction(); + CharacterDatabase.PExecute("DELETE FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + CharacterDatabase.PExecute("DELETE FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid)); + CharacterDatabase.CommitTransaction(); + + // created + sLog.outDebug("TURN IN PETITION GUID %u", GUID_LOPART(petitionguid)); + + data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4); + data << (uint32)PETITION_TURN_OK; + SendPacket(&data); +} + +void WorldSession::HandlePetitionShowListOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data, 8); + + sLog.outDebug("Received CMSG_PETITION_SHOWLIST"); // ok + //recv_data.hexlike(); + + uint64 guid; + recv_data >> guid; + + SendPetitionShowList(guid); +} + +void WorldSession::SendPetitionShowList(uint64 guid) +{ + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_PETITIONER); + if (!pCreature) + { + sLog.outDebug("WORLD: HandlePetitionShowListOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); + return; + } + + // remove fake death + if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + + uint8 count = 0; + if(pCreature->isTabardDesigner()) + count = 1; + else + count = 3; + + WorldPacket data(SMSG_PETITION_SHOWLIST, 8+1+4*6); + data << guid; // npc guid + data << count; // count + if(count == 1) + { + data << uint32(1); // index + data << uint32(GUILD_CHARTER); // charter entry + data << uint32(16161); // charter display id + data << uint32(GUILD_CHARTER_COST); // charter cost + data << uint32(0); // unknown + data << uint32(9); // required signs? + } + else + { + // 2v2 + data << uint32(1); // index + data << uint32(ARENA_TEAM_CHARTER_2v2); // charter entry + data << uint32(16161); // charter display id + data << uint32(ARENA_TEAM_CHARTER_2v2_COST); // charter cost + data << uint32(2); // unknown + data << uint32(2); // required signs? + // 3v3 + data << uint32(2); // index + data << uint32(ARENA_TEAM_CHARTER_3v3); // charter entry + data << uint32(16161); // charter display id + data << uint32(ARENA_TEAM_CHARTER_3v3_COST); // charter cost + data << uint32(3); // unknown + data << uint32(3); // required signs? + // 5v5 + data << uint32(3); // index + data << uint32(ARENA_TEAM_CHARTER_5v5); // charter entry + data << uint32(16161); // charter display id + data << uint32(ARENA_TEAM_CHARTER_5v5_COST); // charter cost + data << uint32(5); // unknown + data << uint32(5); // required signs? + } + //for(uint8 i = 0; i < count; i++) + //{ + // data << uint32(i); // index + // data << uint32(GUILD_CHARTER); // charter entry + // data << uint32(16161); // charter display id + // data << uint32(GUILD_CHARTER_COST+i); // charter cost + // data << uint32(0); // unknown + // data << uint32(9); // required signs? + //} + SendPacket(&data); + sLog.outDebug("Sent SMSG_PETITION_SHOWLIST"); +} diff --git a/src/game/Player.cpp b/src/game/Player.cpp new file mode 100644 index 00000000000..f93b44ba732 --- /dev/null +++ b/src/game/Player.cpp @@ -0,0 +1,18130 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 + +#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; ic_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 and GM if set in config accept whispers by default + if(GetSession()->GetSecurity() == SEC_PLAYER || sWorld.getConfig(CONFIG_GM_WISPERING_TO)) + 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].bgType = 0; + m_bgBattleGroundQueueID[j].invited = false; + } + m_bgTeam = 0; + + m_logintime = time(NULL); + m_Last_tick = m_logintime; + m_WeaponProficiency = 0; + m_ArmorProficiency = 0; + m_canParry = 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 + SetUInt32Value( UNIT_FIELD_LEVEL, sWorld.getConfig(CONFIG_START_PLAYER_LEVEL) ); + + // 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::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]; + } + + UpdateBlockPercentage(); + + 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 have water breath , then remove bar + if(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::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(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(isGameMaster()) + 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)... + if(!InBattleGround() && mEntry->IsBattleGround() && !GetSession()->GetSecurity()) + 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; + } + } + + SetSemaphoreTeleport(false); + + if(!GetSession()->PlayerLogout()) + 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(); + + // 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); + + // Base parry percents + SetFloatValue(PLAYER_PARRY_PERCENTAGE, 5.0f); + + //Base block percentage + SetFloatValue(PLAYER_BLOCK_PERCENTAGE, 5.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(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); + + for(uint16 i = 0; i < INVENTORY_SLOT_BAG_END; i++) + { + updateVisualBits.SetBit((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + i*2)); + updateVisualBits.SetBit((uint16)(PLAYER_FIELD_INV_SLOT_HEAD + (i*2) + 1)); + } + //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->Id)) + return TRAINER_SPELL_GRAY; + + // check race/class requirement + if(!IsSpellFitByClassAndRace(trainer_spell->spell->Id)) + return TRAINER_SPELL_RED; + + // check level requirement + if(getLevel() < ( trainer_spell->reqlevel ? trainer_spell->reqlevel : trainer_spell->spell->spellLevel)) + return TRAINER_SPELL_RED; + + if(SpellChainNode const* spell_chain = spellmgr.GetSpellChainNode(trainer_spell->spell->Id)) + { + // 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; + + // secondary prof. or not prof. spell + uint32 skill = trainer_spell->spell->EffectMiscValue[1]; + + if(trainer_spell->spell->Effect[1] != SPELL_EFFECT_SKILL || !IsPrimaryProfessionSkill(skill)) + return TRAINER_SPELL_GREEN; + + // check primary prof. limit + if(spellmgr.IsPrimaryProfessionFirstRankSpell(trainer_spell->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); + } + + // 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.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(!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(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) + { + UpdateBlockPercentage(); + } + } +} + +// 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(isecond->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(pVictim->GetEntry()); + + 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->reputration_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->reputration_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) +{ + // '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(); + + 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: 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: 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: + // victim_rank [5..19] HK: + // 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); + 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='"<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='"<Fetch()[0].GetUInt32(); + delete result; + return v; + } + else + return 0; +} + +uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type) +{ + // need fix it! + QueryResult *result = CharacterDatabase.PQuery("SELECT arenateamid FROM arena_team_member WHERE guid='%u'", GUID_LOPART(guid)); + if(result) + { + // init id to 0, check the arena type before assigning a value to id + uint32 id = 0; + 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 + id = (*result)[0].GetUInt32(); + break; + } + } + } while(result->NextRow()); + delete result; + 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='"<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='"<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='"<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<SendPacket(&data); + data.Initialize(SMSG_SPELL_COOLDOWN, 17); + data<opponent->GetGUID(); + data<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 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; iopponent->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; iopponent->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(); + + //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 qualityGroupLoot(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 + data << uint32(0xa0f) << uint32(0x0); // 7 + data << uint32(0xa10) << uint32(0x0); // 8 + data << uint32(0xa11) << uint32(0x0); // 9 + break; + case 3702: // Blade's Edge Arena + data << uint32(0x9f0) << uint32(0x0); // 7 + data << uint32(0x9f1) << uint32(0x0); // 8 + data << uint32(0x9f3) << uint32(0x0); // 9 + break; + case 3968: // Ruins of Lordaeron + data << uint32(0xbb8) << uint32(0x0); // 7 + data << uint32(0xbb9) << uint32(0x0); // 8 + data << uint32(0xbba) << uint32(0x0); // 9 + 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()); + 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;kGetEntry(), 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 ) + { + 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 + + 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;xGetEnchantmentId(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* pQuestMenu = PlayerTalkClass->GetQuestMenu(); + if( !pQuestMenu || pQuestMenu->MenuItemCount() < 1 ) + return; + + uint32 status = pQuestMenu->GetItem(0).m_qIcon; + if ( pQuestMenu->MenuItemCount() == 1 ) + { + // Auto open -- maybe also should verify there is no greeting + uint32 quest_id = pQuestMenu->GetItem(0).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 ); + } + } + 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(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)) + { + case 0: // disable + break; + case 1: // enable + SetGameMaster(true); + break; + case 2: // save state + if(gmstate) + SetGameMaster(true); + break; + default: + break; + } + } + + //Unmount Player from previous mount, so speed bug with mount is no more... + if(IsMounted()) + { + Unmount(); + RemoveSpellsCausingAura(SPELL_AURA_MOUNTED); + } + + _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 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 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::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(); + + // Must saved before enter into BattleGround + if(InBattleGround()) + 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 << (isGameMaster()? 1 : 0); + + 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::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::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='"<= 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::PlaySound(uint32 Sound, bool OnlySelf) +{ + WorldPacket data(SMSG_PLAY_SOUND, 4); + data << Sound; + if (OnlySelf) + GetSession()->SendPacket( &data ); + else + SendMessageToSet( &data, true ); +} + +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 +{ + bool pre = (msgtype==CHAT_MSG_EMOTE); + + *data << (uint8)msgtype; + *data << (uint32)language; + *data << (uint64)GetGUID(); + *data << (uint32)language; //language 2.1.0 ? + *data << (uint64)GetGUID(); + *data << (uint32)(text.length()+1+(pre?3:0)); + if(pre) + data->append("%s ",3); + *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;icharges == -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 stealthedUnits; + + CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + MaNGOS::AnyStealthedCheck u_check; + MaNGOS::UnitListSearcher searcher(stealthedUnits, u_check); + + TypeContainerVisitor, WorldTypeMapContainer > world_unit_searcher(searcher); + TypeContainerVisitor, GridTypeMapContainer > grid_unit_searcher(searcher); + + CellLock 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::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 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; + } + + // load vendor items if not yet + pCreature->LoadGoods(); + + CreatureItem* crItem = pCreature->FindItem(item); + if(!crItem) + { + SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0); + return false; + } + + if( crItem->maxcount != 0 && crItem->count < 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 )) + { + if( crItem->maxcount != 0 ) + crItem->count -= pProto->BuyCount * count; + + WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4)); + data << pCreature->GetGUID(); + data << (uint32)crItem->id; // entry + data << (uint32)crItem->count; + data << (uint32)count; + GetSession()->SendPacket(&data); + + SendNewItem(it, 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 )) + { + if( crItem->maxcount != 0 ) + crItem->count -= pProto->BuyCount * count; + + WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4)); + data << pCreature->GetGUID(); + data << (uint32)crItem->id; // entry + data << (uint32)crItem->count; + data << (uint32)count; + GetSession()->SendPacket(&data); + + SendNewItem(it, count, true, false, false); + + AutoUnequipOffhandIfNeed(); + } + } + else + { + SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL ); + return false; + } + + return crItem->maxcount!=0?true:false; +} + +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_ratingSendPacket(&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(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 use them as count, else use from Condition + uint32 _cmp_gem = Condition->CompareColor[i] ? curcount[Condition->CompareColor[i] - 1]: Condition->Value[i]; + + switch(Condition->Comparator[i]) + { + case 2: // requires less than ( || ) gems + activate &= (_cur_gem < _cmp_gem) ? true : false; + break; + case 3: // requires more than ( || ) gems + activate &= (_cur_gem > _cmp_gem) ? true : false; + break; + case 5: // requires at least than ( || ) 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 +inline void UpdateVisibilityOf_helper(std::set& s64, T* target) +{ + s64.insert(target->GetGUID()); +} + +template<> +inline void UpdateVisibilityOf_helper(std::set& s64, GameObject* target) +{ + if(!target->IsTransport()) + s64.insert(target->GetGUID()); +} + +template +void Player::UpdateVisibilityOf(T* target, UpdateData& data, UpdateDataMapType& data_updates, std::set& 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& visibleNow); +template void Player::UpdateVisibilityOf(Creature* target, UpdateData& data, UpdateDataMapType& data_updates, std::set& visibleNow); +template void Player::UpdateVisibilityOf(Corpse* target, UpdateData& data, UpdateDataMapType& data_updates, std::set& visibleNow); +template void Player::UpdateVisibilityOf(GameObject* target, UpdateData& data, UpdateDataMapType& data_updates, std::set& visibleNow); +template void Player::UpdateVisibilityOf(DynamicObject* target, UpdateData& data, UpdateDataMapType& data_updates, std::set& 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 ); +} + +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_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::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; jskillId!=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 +{ + BattleGround *bg = sBattleGroundMgr.GetBattleGround(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::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) && 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)) + 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 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; +} + +bool ItemPosCount::isContainedIn(ItemPosCountVec &vec) +{ + for(ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr) + { + if(itr->pos == this->pos/* && itr->count == this.count*/) + { + return true; + } + } + return false; +} + diff --git a/src/game/Player.h b/src/game/Player.h new file mode 100644 index 00000000000..d30d8e56622 --- /dev/null +++ b/src/game/Player.h @@ -0,0 +1,2310 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _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 +#include + +struct Mail; +class Channel; +class DynamicObject; +class Creature; +class Pet; +class PlayerMenu; +class Transport; +class UpdateMask; +class PlayerSocial; + +typedef std::deque 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 PlayerSpellMap; +typedef std::list SpellModList; + +struct SpellCooldown +{ + time_t end; + uint16 itemid; +}; + +typedef std::map 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 ActionButtonList; + +typedef std::pair CreateSpellPair; + +struct PlayerCreateInfoItem +{ + PlayerCreateInfoItem(uint32 id, uint32 amount) : item_id(id), item_amount(amount) {} + + uint32 item_id; + uint32 item_amount; +}; + +typedef std::list 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 spell; + std::list 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 FactionStateList; + +typedef std::map ForcedReactions; + +typedef std::set 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 EnchantDurationList; +typedef std::list 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< 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&); + uint16 pos; + uint8 count; +}; +typedef std::vector 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 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 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 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 &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 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 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); + + //Low Level Packets + void PlaySound(uint32 Sound, bool OnlySelf); + //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); + 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) { m_canParry = 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 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; + + uint32 GetBattleGroundQueueId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgType; } + uint32 GetBattleGroundQueueIndex(uint32 bgType) const + { + for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + if (m_bgBattleGroundQueueID[i].bgType == bgType) + return i; + return PLAYER_MAX_BATTLEGROUND_QUEUES; + } + bool IsInvitedForBattleGroundType(uint32 bgType) const + { + for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + if (m_bgBattleGroundQueueID[i].bgType == bgType) + return m_bgBattleGroundQueueID[i].invited; + return PLAYER_MAX_BATTLEGROUND_QUEUES; + } + bool InBattleGroundQueueForBattleGroundType(uint32 bgType) const + { + return GetBattleGroundQueueIndex(bgType) < 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].bgType == 0 || m_bgBattleGroundQueueID[i].bgType == val) + { + m_bgBattleGroundQueueID[i].bgType = val; + m_bgBattleGroundQueueID[i].invited = false; + return i; + } + } + return PLAYER_MAX_BATTLEGROUND_QUEUES; + } + void RemoveBattleGroundQueueId(uint32 val) + { + for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + { + if (m_bgBattleGroundQueueID[i].bgType == val) + { + m_bgBattleGroundQueueID[i].bgType = 0; + m_bgBattleGroundQueueID[i].invited = false; + return; + } + } + } + void SetInviteForBattleGroundType(uint32 bgType) + { + for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + if (m_bgBattleGroundQueueID[i].bgType == bgType) + m_bgBattleGroundQueueID[i].invited = true; + } + + 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; + + /*********************************************************/ + /*** 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 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 + void UpdateVisibilityOf(T* target, UpdateData& data, UpdateDataMapType& data_updates, std::set& 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 &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 bgType; + bool invited; + }; + 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 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 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 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 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_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 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 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/PlayerDump.cpp b/src/game/PlayerDump.cpp new file mode 100644 index 00000000000..413f006b501 --- /dev/null +++ b/src/game/PlayerDump.cpp @@ -0,0 +1,605 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "PlayerDump.h" +#include "Database/DatabaseEnv.h" +#include "Database/SQLStorage.h" +#include "UpdateFields.h" +#include "ObjectMgr.h" + +// Character Dump tables +#define DUMP_TABLE_COUNT 20 + +struct DumpTable +{ + char const* name; + DumpTableType type; +}; + +static DumpTable dumpTables[DUMP_TABLE_COUNT] = +{ + { "characters", DTT_CHARACTER }, + { "character_queststatus", DTT_CHAR_TABLE }, + { "character_reputation", DTT_CHAR_TABLE }, + { "character_spell", DTT_CHAR_TABLE }, + { "character_spell_cooldown", DTT_CHAR_TABLE }, + { "character_action", DTT_CHAR_TABLE }, + { "character_aura", DTT_CHAR_TABLE }, + { "character_homebind", DTT_CHAR_TABLE }, + { "character_ticket", DTT_CHAR_TABLE }, + { "character_inventory", DTT_INVENTORY }, + { "mail", DTT_MAIL }, + { "mail_items", DTT_MAIL_ITEM }, + { "item_instance", DTT_ITEM }, + { "character_gifts", DTT_ITEM_GIFT }, + { "item_text", DTT_ITEM_TEXT }, + { "character_pet", DTT_PET }, + { "pet_aura", DTT_PET_TABLE }, + { "pet_spell", DTT_PET_TABLE }, + { "pet_spell_cooldown", DTT_PET_TABLE }, +}; + +// Low level functions +static bool findtoknth(std::string &str, int n, std::string::size_type &s, std::string::size_type &e) +{ + int i; s = e = 0; + std::string::size_type size = str.size(); + for(i = 1; s < size && i < n; s++) if(str[s] == ' ') ++i; + if (i < n) + return false; + + e = str.find(' ', s); + + return e != std::string::npos; +} + +std::string gettoknth(std::string &str, int n) +{ + std::string::size_type s = 0, e = 0; + if(!findtoknth(str, n, s, e)) + return ""; + + return str.substr(s, e-s); +} + +bool findnth(std::string &str, int n, std::string::size_type &s, std::string::size_type &e) +{ + s = str.find("VALUES ('")+9; + if (s == std::string::npos) return false; + + do + { + e = str.find("'",s); + if (e == std::string::npos) return false; + } while(str[e-1] == '\\'); + + for(int i = 1; i < n; i++) + { + do + { + s = e+4; + e = str.find("'",s); + if (e == std::string::npos) return false; + } while (str[e-1] == '\\'); + } + return true; +} + +std::string gettablename(std::string &str) +{ + std::string::size_type s = 13; + std::string::size_type e = str.find(_TABLE_SIM_, s); + if (e == std::string::npos) + return ""; + + return str.substr(s, e-s); +} + +bool changenth(std::string &str, int n, const char *with, bool insert = false, bool nonzero = false) +{ + std::string::size_type s, e; + if(!findnth(str,n,s,e)) + return false; + + if(nonzero && str.substr(s,e-s) == "0") + return true; // not an error + if(!insert) + str.replace(s,e-s, with); + else + str.insert(s, with); + + return true; +} + +std::string getnth(std::string &str, int n) +{ + std::string::size_type s, e; + if(!findnth(str,n,s,e)) + return ""; + + return str.substr(s, e-s); +} + +bool changetoknth(std::string &str, int n, const char *with, bool insert = false, bool nonzero = false) +{ + std::string::size_type s = 0, e = 0; + if(!findtoknth(str, n, s, e)) + return false; + if(nonzero && str.substr(s,e-s) == "0") + return true; // not an error + if(!insert) + str.replace(s, e-s, with); + else + str.insert(s, with); + + return true; +} + +uint32 registerNewGuid(uint32 oldGuid, std::map &guidMap, uint32 hiGuid) +{ + std::map::iterator itr = guidMap.find(oldGuid); + if(itr != guidMap.end()) + return itr->second; + + uint32 newguid = hiGuid + guidMap.size(); + guidMap[oldGuid] = newguid; + return newguid; +} + +bool changeGuid(std::string &str, int n, std::map &guidMap, uint32 hiGuid, bool nonzero = false) +{ + char chritem[20]; + uint32 oldGuid = atoi(getnth(str, n).c_str()); + if (nonzero && oldGuid == 0) + return true; // not an error + + uint32 newGuid = registerNewGuid(oldGuid, guidMap, hiGuid); + snprintf(chritem, 20, "%d", newGuid); + + return changenth(str, n, chritem, false, nonzero); +} + +bool changetokGuid(std::string &str, int n, std::map &guidMap, uint32 hiGuid, bool nonzero = false) +{ + char chritem[20]; + uint32 oldGuid = atoi(gettoknth(str, n).c_str()); + if (nonzero && oldGuid == 0) + return true; // not an error + + uint32 newGuid = registerNewGuid(oldGuid, guidMap, hiGuid); + snprintf(chritem, 20, "%d", newGuid); + + return changetoknth(str, n, chritem, false, nonzero); +} + +std::string CreateDumpString(char const* tableName, QueryResult *result) +{ + if(!tableName || !result) return ""; + std::ostringstream ss; + ss << "INSERT INTO "<< _TABLE_SIM_ << tableName << _TABLE_SIM_ << " VALUES ("; + Field *fields = result->Fetch(); + for(uint32 i = 0; i < result->GetFieldCount(); i++) + { + if (i == 0) ss << "'"; + else ss << ", '"; + + std::string s = fields[i].GetCppString(); + CharacterDatabase.escape_string(s); + ss << s; + + ss << "'"; + } + ss << ");"; + return ss.str(); +} + +std::string PlayerDumpWriter::GenerateWhereStr(char const* field, uint32 guid) +{ + std::ostringstream wherestr; + wherestr << field << " = '" << guid << "'"; + return wherestr.str(); +} + +std::string PlayerDumpWriter::GenerateWhereStr(char const* field, GUIDs const& guids, GUIDs::const_iterator& itr) +{ + std::ostringstream wherestr; + wherestr << field << " IN ('"; + for(; itr != guids.end(); ++itr) + { + wherestr << *itr; + + if(wherestr.str().size() > MAX_QUERY_LEN - 50) // near to max query + { + ++itr; + break; + } + + GUIDs::const_iterator itr2 = itr; + if(++itr2 != guids.end()) + wherestr << "','"; + } + wherestr << "')"; + return wherestr.str(); +} + +void StoreGUID(QueryResult *result,uint32 field,std::set& guids) +{ + Field* fields = result->Fetch(); + uint32 guid = fields[field].GetUInt32(); + if(guid) + guids.insert(guid); +} + +void StoreGUID(QueryResult *result,uint32 data,uint32 field, std::set& guids) +{ + Field* fields = result->Fetch(); + std::string dataStr = fields[data].GetCppString(); + uint32 guid = atoi(gettoknth(dataStr, field).c_str()); + if(guid) + guids.insert(guid); +} + +// Writing - High-level functions +bool PlayerDumpWriter::DumpTable(std::string& dump, uint32 guid, char const*tableFrom, char const*tableTo, DumpTableType type) +{ + if (!tableFrom || !tableTo) + return false; + + GUIDs const* guids = NULL; + char const* fieldname = NULL; + + switch ( type ) + { + case DTT_ITEM: fieldname = "guid"; guids = &items; break; + case DTT_ITEM_GIFT: fieldname = "item_guid"; guids = &items; break; + case DTT_PET: fieldname = "owner"; break; + case DTT_PET_TABLE: fieldname = "guid"; guids = &pets; break; + case DTT_MAIL: fieldname = "receiver"; break; + case DTT_MAIL_ITEM: fieldname = "mail_id"; guids = &mails; break; + case DTT_ITEM_TEXT: fieldname = "id"; guids = &texts; break; + default: fieldname = "guid"; break; + } + + // for guid set stop if set is empty + if(guids && guids->empty()) + return true; // nothing to do + + // setup for guids case start position + GUIDs::const_iterator guids_itr; + if(guids) + guids_itr = guids->begin(); + + do + { + std::string wherestr; + + if(guids) // set case, get next guids string + wherestr = GenerateWhereStr(fieldname,*guids,guids_itr); + else // not set case, get single guid string + wherestr = GenerateWhereStr(fieldname,guid); + + QueryResult *result = CharacterDatabase.PQuery("SELECT * FROM %s WHERE %s", tableFrom, wherestr.c_str()); + if(!result) + return false; + + do + { + // collect guids + switch ( type ) + { + case DTT_INVENTORY: + StoreGUID(result,3,items); break; // item guid collection + case DTT_ITEM: + StoreGUID(result,0,ITEM_FIELD_ITEM_TEXT_ID,texts); break; + // item text id collection + case DTT_PET: + StoreGUID(result,0,pets); break; // pet guid collection + case DTT_MAIL: + StoreGUID(result,0,mails); // mail id collection + StoreGUID(result,6,texts); break; // item text id collection + case DTT_MAIL_ITEM: + StoreGUID(result,1,items); break; // item guid collection + default: break; + } + + dump += CreateDumpString(tableTo, result); + dump += "\n"; + } + while (result->NextRow()); + + delete result; + } + while(guids && guids_itr != guids->end()); // not set case iterate single time, set case iterate for all guids + + return true; +} + +std::string PlayerDumpWriter::GetDump(uint32 guid) +{ + std::string dump; + for(int i = 0; i < DUMP_TABLE_COUNT; i++) + DumpTable(dump, guid, dumpTables[i].name, dumpTables[i].name, dumpTables[i].type); + + // TODO: Add instance/group/gifts.. + // TODO: Add a dump level option to skip some non-important tables + + return dump; +} + +bool PlayerDumpWriter::WriteDump(std::string file, uint32 guid) +{ + FILE *fout = fopen(file.c_str(), "w"); + if (!fout) { sLog.outError("Failed to open file!\r\n"); return false; } + + std::string dump = GetDump(guid); + + fprintf(fout,"%s\n",dump.c_str()); + fclose(fout); + return true; +} + +// Reading - High-level functions +#define ROLLBACK {CharacterDatabase.RollbackTransaction(); fclose(fin); return false;} + +bool PlayerDumpReader::LoadDump(std::string file, uint32 account, std::string name, uint32 guid) +{ + // check character count + { + QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", account); + uint8 charcount = 0; + if ( result ) + { + Field *fields=result->Fetch(); + charcount = fields[0].GetUInt8(); + delete result; + + if (charcount >= 10) + { + return false; + } + } + } + FILE *fin = fopen(file.c_str(), "r"); + if(!fin) return false; + + QueryResult * result = NULL; + char newguid[20], chraccount[20], newpetid[20], currpetid[20], lastpetid[20]; + + // make sure the same guid doesn't already exist and is safe to use + bool incHighest = true; + if(guid != 0 && guid < objmgr.m_hiCharGuid) + { + result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE guid = '%d'", guid); + if (result) + { + guid = objmgr.m_hiCharGuid; // use first free if exists + delete result; + } + else incHighest = false; + } + else guid = objmgr.m_hiCharGuid; + + // normalize the name if specified and check if it exists + if(!normalizePlayerName(name)) + name = ""; + + if(ObjectMgr::IsValidName(name,true)) + { + CharacterDatabase.escape_string(name); // for safe, we use name only for sql quearies anyway + result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE name = '%s'", name.c_str()); + if (result) + { + name = ""; // use the one from the dump + delete result; + } + } + else name = ""; + + // name encoded or empty + + snprintf(newguid, 20, "%d", guid); + snprintf(chraccount, 20, "%d", account); + snprintf(newpetid, 20, "%d", objmgr.GeneratePetNumber()); + snprintf(lastpetid, 20, "%s", ""); + + std::map items; + std::map mails; + char buf[32000] = ""; + + typedef std::map PetIds; // old->new petid relation + typedef PetIds::value_type PetIdsPair; + PetIds petids; + + CharacterDatabase.BeginTransaction(); + while(!feof(fin)) + { + if(!fgets(buf, 32000, fin)) + { + if(feof(fin)) break; + sLog.outError("LoadPlayerDump: File read error!"); + ROLLBACK; + } + + std::string line; line.assign(buf); + + // skip empty strings + if(line.find_first_not_of(" \t\n\r\7")==std::string::npos) + continue; + + // determine table name and load type + std::string tn = gettablename(line); + if(tn.empty()) + { + sLog.outError("LoadPlayerDump: Can't extract table name from line: '%s'!", line.c_str()); + ROLLBACK; + } + + DumpTableType type; + uint8 i; + for(i = 0; i < DUMP_TABLE_COUNT; i++) + { + if (tn == dumpTables[i].name) + { + type = dumpTables[i].type; + break; + } + } + + if (i == DUMP_TABLE_COUNT) + { + sLog.outError("LoadPlayerDump: Unknown table: '%s'!", tn.c_str()); + ROLLBACK; + } + + // change the data to server values + switch(type) + { + case DTT_CHAR_TABLE: + if(!changenth(line, 1, newguid)) ROLLBACK; + break; + + case DTT_CHARACTER: // character t. + { + if(!changenth(line, 1, newguid)) ROLLBACK; + + // guid, data field:guid, items + if(!changenth(line, 2, chraccount)) ROLLBACK; + std::string vals = getnth(line, 3); + if(!changetoknth(vals, OBJECT_FIELD_GUID+1, newguid)) ROLLBACK; + for(uint16 field = PLAYER_FIELD_INV_SLOT_HEAD; field < PLAYER_FARSIGHT; field++) + if(!changetokGuid(vals, field+1, items, objmgr.m_hiItemGuid, true)) ROLLBACK; + if(!changenth(line, 3, vals.c_str())) ROLLBACK; + if (name == "") + { + // check if the original name already exists + name = getnth(line, 4); + CharacterDatabase.escape_string(name); + + result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE name = '%s'", name.c_str()); + if (result) + { + delete result; + // rename on login: `at_login` field 30 in raw field list + if(!changenth(line, 30, "1")) ROLLBACK; + } + } + else if(!changenth(line, 4, name.c_str())) ROLLBACK; + + break; + } + case DTT_INVENTORY: // character_inventory t. + { + if(!changenth(line, 1, newguid)) ROLLBACK; + + // bag, item + if(!changeGuid(line, 2, items, objmgr.m_hiItemGuid, true)) ROLLBACK; + if(!changeGuid(line, 4, items, objmgr.m_hiItemGuid)) ROLLBACK; + break; + } + case DTT_ITEM: // item_instance t. + { + // item, owner, data field:item, owner guid + if(!changeGuid(line, 1, items, objmgr.m_hiItemGuid)) ROLLBACK; + if(!changenth(line, 2, newguid)) ROLLBACK; + std::string vals = getnth(line,3); + if(!changetokGuid(vals, OBJECT_FIELD_GUID+1, items, objmgr.m_hiItemGuid)) ROLLBACK; + if(!changetoknth(vals, ITEM_FIELD_OWNER+1, newguid)) ROLLBACK; + if(!changenth(line, 3, vals.c_str())) ROLLBACK; + break; + } + case DTT_ITEM_GIFT: // character_gift + { + // guid,item_guid, + if(!changenth(line, 1, newguid)) ROLLBACK; + if(!changeGuid(line, 2, items, objmgr.m_hiItemGuid)) ROLLBACK; + break; + } + case DTT_PET: // character_pet t + { + //store a map of old pet id to new inserted pet id for use by type 5 tables + snprintf(currpetid, 20, "%s", getnth(line, 1).c_str()); + if(strlen(lastpetid)==0) snprintf(lastpetid, 20, "%s", currpetid); + if(strcmp(lastpetid,currpetid)!=0) + { + snprintf(newpetid, 20, "%d", objmgr.GeneratePetNumber()); + snprintf(lastpetid, 20, "%s", currpetid); + } + + std::map :: const_iterator petids_iter = petids.find(atoi(currpetid)); + + if(petids_iter == petids.end()) + { + petids.insert(PetIdsPair(atoi(currpetid), atoi(newpetid))); + } + + // item, entry, owner, ... + if(!changenth(line, 1, newpetid)) ROLLBACK; + if(!changenth(line, 3, newguid)) ROLLBACK; + + break; + } + case DTT_PET_TABLE: // pet_aura, pet_spell, pet_spell_cooldown t + { + snprintf(currpetid, 20, "%s", getnth(line, 1).c_str()); + + // lookup currpetid and match to new inserted pet id + std::map :: const_iterator petids_iter = petids.find(atoi(currpetid)); + if(petids_iter == petids.end()) ROLLBACK; // couldn't find new inserted id + + snprintf(newpetid, 20, "%d", petids_iter->second); + + if(!changenth(line, 1, newpetid)) ROLLBACK; + + break; + } + case DTT_MAIL: // mail + { + // id,messageType,stationery,sender,receiver + if(!changeGuid(line, 1, mails, objmgr.m_mailid)) ROLLBACK; + if(!changenth(line, 5, newguid)) ROLLBACK; + break; + } + case DTT_MAIL_ITEM: // mail_items + { + // mail_id,item_guid,item_template,receiver + if(!changeGuid(line, 1, mails, objmgr.m_mailid)) ROLLBACK; + if(!changeGuid(line, 2, items, objmgr.m_hiItemGuid)) ROLLBACK; + if(!changenth(line, 4, newguid)) ROLLBACK; + break; + } + default: + sLog.outError("Unknown dump table type: %u",type); + break; + } + + if(!CharacterDatabase.Execute(line.c_str())) ROLLBACK; + } + + CharacterDatabase.CommitTransaction(); + + objmgr.m_hiItemGuid += items.size(); + objmgr.m_mailid += mails.size(); + + if(incHighest) + ++objmgr.m_hiCharGuid; + + fclose(fin); + + return true; +} diff --git a/src/game/PlayerDump.h b/src/game/PlayerDump.h new file mode 100644 index 00000000000..dfcb2e8b528 --- /dev/null +++ b/src/game/PlayerDump.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PLAYER_DUMP_H +#define _PLAYER_DUMP_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 +#include +#include + +enum DumpTableType +{ + DTT_CHARACTER, // // characters + + DTT_CHAR_TABLE, // // character_action, character_aura, character_homebind, + // character_queststatus, character_reputation, + // character_spell, character_spell_cooldown, character_ticket, + // character_tutorial + + DTT_INVENTORY, // -> item guids collection // character_inventory + + DTT_MAIL, // -> mail ids collection // mail + // -> item_text + + DTT_MAIL_ITEM, // <- mail ids // mail_items + // -> item guids collection + + DTT_ITEM, // <- item guids // item_instance + // -> item_text + + DTT_ITEM_GIFT, // <- item guids // character_gifts + + DTT_PET, // -> pet guids collection // character_pet + DTT_PET_TABLE, // <- pet guids // pet_aura, pet_spell, pet_spell_cooldown + DTT_ITEM_TEXT, // <- item_text // item_text +}; + +class PlayerDump +{ + protected: + PlayerDump() {} +}; + +class PlayerDumpWriter : public PlayerDump +{ + public: + PlayerDumpWriter() {} + + std::string GetDump(uint32 guid); + bool WriteDump(std::string file, uint32 guid); + private: + typedef std::set GUIDs; + + bool DumpTable(std::string& dump, uint32 guid, char const*tableFrom, char const*tableTo, DumpTableType type); + std::string GenerateWhereStr(char const* field, GUIDs const& guids, GUIDs::const_iterator& itr); + std::string GenerateWhereStr(char const* field, uint32 guid); + + GUIDs pets; + GUIDs mails; + GUIDs items; + GUIDs texts; +}; + +class PlayerDumpReader : public PlayerDump +{ + public: + PlayerDumpReader() {} + + bool LoadDump(std::string file, uint32 account, std::string name, uint32 guid); +}; + +#endif diff --git a/src/game/PointMovementGenerator.cpp b/src/game/PointMovementGenerator.cpp new file mode 100644 index 00000000000..c784c799351 --- /dev/null +++ b/src/game/PointMovementGenerator.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PointMovementGenerator.h" +#include "Errors.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "MapManager.h" +#include "DestinationHolderImp.h" + +//----- Point Movement Generator +template +void PointMovementGenerator::Initialize(T &unit) +{ + unit.StopMoving(); + Traveller traveller(unit); + i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z); +} + +template +bool PointMovementGenerator::Update(T &unit, const uint32 &diff) +{ + if(!&unit) + return false; + + if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED)) + return true; + + Traveller traveller(unit); + + i_destinationHolder.UpdateTraveller(traveller, diff, false); + + if(i_destinationHolder.HasArrived()) + { + unit.StopMoving(); + MovementInform(unit); + return false; + } + + return true; +} + +template +void PointMovementGenerator::MovementInform(T &unit) +{ +} + +template <> void PointMovementGenerator::MovementInform(Creature &unit) +{ + unit.AI()->MovementInform(POINT_MOTION_TYPE, id); +} + +template void PointMovementGenerator::Initialize(Player&); +template bool PointMovementGenerator::Update(Player &, const uint32 &diff); +template void PointMovementGenerator::MovementInform(Player&); + +template void PointMovementGenerator::Initialize(Creature&); +template bool PointMovementGenerator::Update(Creature&, const uint32 &diff); diff --git a/src/game/PointMovementGenerator.h b/src/game/PointMovementGenerator.h new file mode 100644 index 00000000000..7127bbecee5 --- /dev/null +++ b/src/game/PointMovementGenerator.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_POINTMOVEMENTGENERATOR_H +#define MANGOS_POINTMOVEMENTGENERATOR_H + +#include "MovementGenerator.h" +#include "DestinationHolder.h" +#include "Traveller.h" +#include "FollowerReference.h" + +template +class MANGOS_DLL_SPEC PointMovementGenerator +: public MovementGeneratorMedium< T, PointMovementGenerator > +{ + public: + PointMovementGenerator(uint32 _id, float _x, float _y, float _z) : id(_id), + i_x(_x), i_y(_y), i_z(_z), i_nextMoveTime(0) {} + + void Initialize(T &); + void Finalize(T &){} + void Reset(T &unit){unit.StopMoving();} + bool Update(T &, const uint32 &diff); + + void MovementInform(T &); + + MovementGeneratorType GetMovementGeneratorType() { return POINT_MOTION_TYPE; } + + bool GetDestination(float& x, float& y, float& z) const { x=i_x; y=i_y; z=i_z; return true; } + private: + TimeTracker i_nextMoveTime; + float i_x,i_y,i_z; + uint32 id; + DestinationHolder< Traveller > i_destinationHolder; +}; +#endif diff --git a/src/game/QueryHandler.cpp b/src/game/QueryHandler.cpp new file mode 100644 index 00000000000..e04c4a909e4 --- /dev/null +++ b/src/game/QueryHandler.cpp @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Language.h" +#include "Database/DatabaseEnv.h" +#include "Database/DatabaseImpl.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 "NPCHandler.h" +#include "ObjectAccessor.h" +#include "Pet.h" + +void WorldSession::SendNameQueryOpcode(Player *p) +{ + if(!p) + return; + + // guess size + WorldPacket data( SMSG_NAME_QUERY_RESPONSE, (8+1+4+4+4+10) ); + data << p->GetGUID(); + data << p->GetName(); + data << uint8(0); // realm name for cross realm BG usage + data << uint32(p->getRace()); + data << uint32(p->getGender()); + data << uint32(p->getClass()); + if(DeclinedName const* names = p->GetDeclinedNames()) + { + data << uint8(1); // is declined + for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + data << names->name[i]; + } + else + data << uint8(0); // is not declined + + SendPacket(&data); +} + +void WorldSession::SendNameQueryOpcodeFromDB(uint64 guid) +{ + CharacterDatabase.AsyncPQuery(&WorldSession::SendNameQueryOpcodeFromDBCallBack, GetAccountId(), + !sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) ? + // ------- Query Without Declined Names -------- + // 0 1 2 + "SELECT guid, name, SUBSTRING(data, LENGTH(SUBSTRING_INDEX(data, ' ', '%u'))+2, LENGTH(SUBSTRING_INDEX(data, ' ', '%u')) - LENGTH(SUBSTRING_INDEX(data, ' ', '%u'))-1) " + "FROM characters WHERE guid = '%u'" + : + // --------- Query With Declined Names --------- + // 0 1 2 + "SELECT characters.guid, name, SUBSTRING(data, LENGTH(SUBSTRING_INDEX(data, ' ', '%u'))+2, LENGTH(SUBSTRING_INDEX(data, ' ', '%u')) - LENGTH(SUBSTRING_INDEX(data, ' ', '%u'))-1), " + // 3 4 5 6 7 + "genitive, dative, accusative, instrumental, prepositional " + "FROM characters LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid WHERE characters.guid = '%u'", + UNIT_FIELD_BYTES_0, UNIT_FIELD_BYTES_0+1, UNIT_FIELD_BYTES_0, GUID_LOPART(guid)); +} + +void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 accountId) +{ + if(!result) + return; + + WorldSession * session = sWorld.FindSession(accountId); + if(!session) + { + delete result; + return; + } + + Field *fields = result->Fetch(); + uint32 guid = fields[0].GetUInt32(); + std::string name = fields[1].GetCppString(); + uint32 field = 0; + if(name == "") + name = session->GetMangosString(LANG_NON_EXIST_CHARACTER); + else + field = fields[2].GetUInt32(); + + // guess size + WorldPacket data( SMSG_NAME_QUERY_RESPONSE, (8+1+4+4+4+10) ); + data << MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER); + data << name; + data << (uint8)0; + data << (uint32)(field & 0xFF); + data << (uint32)((field >> 16) & 0xFF); + data << (uint32)((field >> 8) & 0xFF); + + // if the first declined name field (3) is empty, the rest must be too + if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) && fields[3].GetCppString() != "") + { + data << (uint8)1; // is declined + for(int i = 3; i < MAX_DECLINED_NAME_CASES+3; ++i) + data << fields[i].GetCppString(); + } + else + data << (uint8)0; // is declined + + session->SendPacket( &data ); + delete result; +} + +void WorldSession::HandleNameQueryOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid; + + recv_data >> guid; + + Player *pChar = objmgr.GetPlayer(guid); + + if (pChar) + SendNameQueryOpcode(pChar); + else + SendNameQueryOpcodeFromDB(guid); +} + +void WorldSession::HandleQueryTimeOpcode( WorldPacket & /*recv_data*/ ) +{ + WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 ); + data << (uint32)time(NULL); + data << (uint32)0; + SendPacket( &data ); +} + +/// Only _static_ data send in this packet !!! +void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+8); + + uint32 entry; + recv_data >> entry; + + CreatureInfo const *ci = objmgr.GetCreatureTemplate(entry); + if (ci) + { + + std::string Name, SubName; + Name = ci->Name; + SubName = ci->SubName; + + int loc_idx = GetSessionDbLocaleIndex(); + if (loc_idx >= 0) + { + CreatureLocale const *cl = objmgr.GetCreatureLocale(entry); + if (cl) + { + if (cl->Name.size() > loc_idx && !cl->Name[loc_idx].empty()) + Name = cl->Name[loc_idx]; + if (cl->SubName.size() > loc_idx && !cl->SubName[loc_idx].empty()) + SubName = cl->SubName[loc_idx]; + } + } + sLog.outDetail("WORLD: CMSG_CREATURE_QUERY '%s' - Entry: %u.", ci->Name, entry); + // guess size + WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 100 ); + data << (uint32)entry; // creature entry + data << Name; + data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4, always empty + data << SubName; + data << ci->IconName; // "Directions" for guard, string for Icons 2.3.0 + data << (uint32)ci->flag1; // flags wdbFeild7=wad flags1 + data << (uint32)ci->type; + data << (uint32)ci->family; // family wdbFeild9 + data << (uint32)ci->rank; // rank wdbFeild10 + data << (uint32)0; // unknown wdbFeild11 + data << (uint32)ci->PetSpellDataId; // Id from CreatureSpellData.dbc wdbField12 + data << (uint32)ci->DisplayID_A; // modelid_male1 + data << (uint32)ci->DisplayID_H; // modelid_female1 ? + data << (uint32)ci->DisplayID_A2; // modelid_male2 ? + data << (uint32)ci->DisplayID_H2; // modelid_femmale2 ? + data << (float)1.0f; // unk + data << (float)1.0f; // unk + data << (uint8)ci->RacialLeader; + SendPacket( &data ); + sLog.outDebug( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE " ); + } + else + { + uint64 guid; + recv_data >> guid; + + sLog.outDebug( "WORLD: CMSG_CREATURE_QUERY - (%u) NO CREATURE INFO! (GUID: %u, ENTRY: %u)", uint32(GUID_LOPART(guid)), guid, entry ); + WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 4 ); + data << uint32(entry | 0x80000000); + SendPacket( &data ); + sLog.outDebug( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE " ); + } +} + +/// Only _static_ data send in this packet !!! +void WorldSession::HandleGameObjectQueryOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+8); + + uint32 entryID; + recv_data >> entryID; + + const GameObjectInfo *info = objmgr.GetGameObjectInfo(entryID); + if(info) + { + + std::string Name; + std::string CastBarCaption; + + Name = info->name; + CastBarCaption = info->castBarCaption; + + int loc_idx = GetSessionDbLocaleIndex(); + if (loc_idx >= 0) + { + GameObjectLocale const *gl = objmgr.GetGameObjectLocale(entryID); + if (gl) + { + if (gl->Name.size() > loc_idx && !gl->Name[loc_idx].empty()) + Name = gl->Name[loc_idx]; + if (gl->CastBarCaption.size() > loc_idx && !gl->CastBarCaption[loc_idx].empty()) + CastBarCaption = gl->CastBarCaption[loc_idx]; + } + } + sLog.outDetail("WORLD: CMSG_GAMEOBJECT_QUERY '%s' - Entry: %u. ", info->name, entryID); + WorldPacket data ( SMSG_GAMEOBJECT_QUERY_RESPONSE, 150 ); + data << entryID; + data << (uint32)info->type; + data << (uint32)info->displayId; + data << Name; + data << uint8(0) << uint8(0) << uint8(0); // name2, name3, name4 + data << uint8(0); // 2.0.3, string + data << CastBarCaption; // 2.0.3, string. Text will appear in Cast Bar when using GO (ex: "Collecting") + data << uint8(0); // 2.0.3, probably string + data.append(info->raw.data,24); + SendPacket( &data ); + sLog.outDebug( "WORLD: Sent CMSG_GAMEOBJECT_QUERY " ); + } + else + { + + uint64 guid; + recv_data >> guid; + + sLog.outDebug( "WORLD: CMSG_GAMEOBJECT_QUERY - (%u) Missing gameobject info for (GUID: %u, ENTRY: %u)", uint32(GUID_LOPART(guid)), guid, entryID ); + WorldPacket data ( SMSG_GAMEOBJECT_QUERY_RESPONSE, 4 ); + data << uint32(entryID | 0x80000000); + SendPacket( &data ); + sLog.outDebug( "WORLD: Sent CMSG_GAMEOBJECT_QUERY " ); + } +} + +void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recv_data*/) +{ + sLog.outDetail("WORLD: Received MSG_CORPSE_QUERY"); + + Corpse *corpse = GetPlayer()->GetCorpse(); + + uint8 found = 1; + if(!corpse) + found = 0; + + WorldPacket data(MSG_CORPSE_QUERY, (1+found*(5*4))); + data << uint8(found); + if(found) + { + data << corpse->GetMapId(); + data << corpse->GetPositionX(); + data << corpse->GetPositionY(); + data << corpse->GetPositionZ(); + data << _player->GetMapId(); + } + SendPacket(&data); +} + +void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+8); + + uint32 textID; + uint64 guid; + GossipText *pGossip; + std::string GossipStr; + + recv_data >> textID; + sLog.outDetail("WORLD: CMSG_NPC_TEXT_QUERY ID '%u'", textID); + + recv_data >> guid; + GetPlayer()->SetUInt64Value(UNIT_FIELD_TARGET, guid); + + pGossip = objmgr.GetGossipText(textID); + + WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size + data << textID; + + 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 = 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; + } + } + + SendPacket( &data ); + + sLog.outDebug( "WORLD: Sent SMSG_NPC_TEXT_UPDATE " ); +} + +void WorldSession::HandlePageQueryOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4); + + uint32 pageID; + + recv_data >> pageID; + sLog.outDetail("WORLD: Received CMSG_PAGE_TEXT_QUERY for pageID '%u'", pageID); + + while (pageID) + { + PageText const *pPage = sPageTextStore.LookupEntry( pageID ); + // guess size + WorldPacket data( SMSG_PAGE_TEXT_QUERY_RESPONSE, 50 ); + data << pageID; + + if (!pPage) + { + data << "Item page missing."; + data << uint32(0); + pageID = 0; + } + else + { + std::string Text = pPage->Text; + + int loc_idx = GetSessionDbLocaleIndex(); + if (loc_idx >= 0) + { + PageTextLocale const *pl = objmgr.GetPageTextLocale(pageID); + if (pl) + { + if (pl->Text.size() > loc_idx && !pl->Text[loc_idx].empty()) + Text = pl->Text[loc_idx]; + } + } + + data << Text; + data << uint32(pPage->Next_Page); + pageID = pPage->Next_Page; + } + SendPacket( &data ); + + sLog.outDebug( "WORLD: Sent SMSG_PAGE_TEXT_QUERY_RESPONSE " ); + } +} diff --git a/src/game/QuestDef.cpp b/src/game/QuestDef.cpp new file mode 100644 index 00000000000..c92bb9b7b6a --- /dev/null +++ b/src/game/QuestDef.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "QuestDef.h" +#include "Player.h" +#include "World.h" + +Quest::Quest(Field * questRecord) +{ + QuestId = questRecord[0].GetUInt32(); + ZoneOrSort = questRecord[1].GetInt32(); + SkillOrClass = questRecord[2].GetInt32(); + MinLevel = questRecord[3].GetUInt32(); + QuestLevel = questRecord[4].GetUInt32(); + Type = questRecord[5].GetUInt32(); + RequiredRaces = questRecord[6].GetUInt32(); + RequiredSkillValue = questRecord[7].GetUInt32(); + RepObjectiveFaction = questRecord[8].GetUInt32(); + RepObjectiveValue = questRecord[9].GetInt32(); + RequiredMinRepFaction = questRecord[10].GetUInt32(); + RequiredMinRepValue = questRecord[11].GetInt32(); + RequiredMaxRepFaction = questRecord[12].GetUInt32(); + RequiredMaxRepValue = questRecord[13].GetInt32(); + SuggestedPlayers = questRecord[14].GetUInt32(); + LimitTime = questRecord[15].GetUInt32(); + QuestFlags = questRecord[16].GetUInt16(); + uint32 SpecialFlags = questRecord[17].GetUInt16(); + CharTitleId = questRecord[18].GetUInt32(); + PrevQuestId = questRecord[19].GetInt32(); + NextQuestId = questRecord[20].GetInt32(); + ExclusiveGroup = questRecord[21].GetInt32(); + NextQuestInChain = questRecord[22].GetUInt32(); + SrcItemId = questRecord[23].GetUInt32(); + SrcItemCount = questRecord[24].GetUInt32(); + SrcSpell = questRecord[25].GetUInt32(); + Title = questRecord[26].GetCppString(); + Details = questRecord[27].GetCppString(); + Objectives = questRecord[28].GetCppString(); + OfferRewardText = questRecord[29].GetCppString(); + RequestItemsText = questRecord[30].GetCppString(); + EndText = questRecord[31].GetCppString(); + + for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + ObjectiveText[i] = questRecord[32+i].GetCppString(); + + for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + ReqItemId[i] = questRecord[36+i].GetUInt32(); + + for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + ReqItemCount[i] = questRecord[40+i].GetUInt32(); + + for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) + ReqSourceId[i] = questRecord[44+i].GetUInt32(); + + for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) + ReqSourceCount[i] = questRecord[48+i].GetUInt32(); + + for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) + ReqSourceRef[i] = questRecord[52+i].GetUInt32(); + + for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + ReqCreatureOrGOId[i] = questRecord[56+i].GetInt32(); + + for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + ReqCreatureOrGOCount[i] = questRecord[60+i].GetUInt32(); + + for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + ReqSpell[i] = questRecord[64+i].GetUInt32(); + + for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) + RewChoiceItemId[i] = questRecord[68+i].GetUInt32(); + + for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) + RewChoiceItemCount[i] = questRecord[74+i].GetUInt32(); + + for (int i = 0; i < QUEST_REWARDS_COUNT; ++i) + RewItemId[i] = questRecord[80+i].GetUInt32(); + + for (int i = 0; i < QUEST_REWARDS_COUNT; ++i) + RewItemCount[i] = questRecord[84+i].GetUInt32(); + + for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) + RewRepFaction[i] = questRecord[88+i].GetUInt32(); + + for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) + RewRepValue[i] = questRecord[93+i].GetInt32(); + + RewOrReqMoney = questRecord[98].GetInt32(); + RewMoneyMaxLevel = questRecord[99].GetUInt32(); + RewSpell = questRecord[100].GetUInt32(); + RewSpellCast = questRecord[101].GetUInt32(); + RewMailTemplateId = questRecord[102].GetUInt32(); + RewMailDelaySecs = questRecord[103].GetUInt32(); + PointMapId = questRecord[104].GetUInt32(); + PointX = questRecord[105].GetFloat(); + PointY = questRecord[106].GetFloat(); + PointOpt = questRecord[107].GetUInt32(); + + for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) + DetailsEmote[i] = questRecord[108+i].GetUInt32(); + + IncompleteEmote = questRecord[112].GetUInt32(); + CompleteEmote = questRecord[113].GetUInt32(); + + for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) + OfferRewardEmote[i] = questRecord[114+i].GetInt32(); + + QuestStartScript = questRecord[118].GetUInt32(); + QuestCompleteScript = questRecord[119].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 new file mode 100644 index 00000000000..ddd24bf57f0 --- /dev/null +++ b/src/game/QuestDef.h @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_QUEST_H +#define MANGOSSERVER_QUEST_H + +#include "Platform/Define.h" +#include "Database/DatabaseEnv.h" + +#include +#include + +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_EVENT = 2, // Not used currently + 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 Title; + std::vector Details; + std::vector Objectives; + std::vector OfferRewardText; + std::vector RequestItemsText; + std::vector EndText; + std::vector< std::vector > 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; } + 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 Objectives.empty(); } + 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 PrevQuests; + PrevQuests prevQuests; + typedef std::vector PrevChainQuests; + PrevChainQuests prevChainQuests; + + // cached data + private: + uint32 m_reqitemscount; + uint32 m_reqCreatureOrGOcount; + uint32 m_rewchoiceitemscount; + uint32 m_rewitemscount; + + // table data + protected: + uint32 QuestId; + 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/QuestHandler.cpp b/src/game/QuestHandler.cpp new file mode 100644 index 00000000000..56ff45d7ef1 --- /dev/null +++ b/src/game/QuestHandler.cpp @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Log.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Opcodes.h" +#include "World.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "GossipDef.h" +#include "QuestDef.h" +#include "ObjectAccessor.h" +#include "ScriptCalls.h" +#include "Group.h" + +void WorldSession::HandleQuestgiverStatusQueryOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid; + recv_data >> guid; + uint8 questStatus = DIALOG_STATUS_NONE; + uint8 defstatus = DIALOG_STATUS_NONE; + + Object* questgiver = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); + if(!questgiver) + { + sLog.outDetail("Error in CMSG_QUESTGIVER_STATUS_QUERY, called for not found questgiver (Typeid: %u GUID: %u)",GuidHigh2TypeId(GUID_HIPART(guid)),GUID_LOPART(guid)); + return; + } + + switch(questgiver->GetTypeId()) + { + case TYPEID_UNIT: + { + sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for npc, guid = %u",uint32(GUID_LOPART(guid)) ); + Creature* cr_questgiver=(Creature*)questgiver; + if( !cr_questgiver->IsHostileTo(_player)) // not show quest status to enemies + { + questStatus = Script->NPCDialogStatus(_player, cr_questgiver); + if( questStatus > 6 ) + questStatus = getDialogStatus(_player, cr_questgiver, defstatus); + } + break; + } + case TYPEID_GAMEOBJECT: + { + sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_STATUS_QUERY for GameObject guid = %u",uint32(GUID_LOPART(guid)) ); + GameObject* go_questgiver=(GameObject*)questgiver; + questStatus = Script->GODialogStatus(_player, go_questgiver); + if( questStatus > 6 ) + questStatus = getDialogStatus(_player, go_questgiver, defstatus); + break; + } + default: + sLog.outError("QuestGiver called for unexpected type %u", questgiver->GetTypeId()); + break; + } + + //inform client about status of quest + _player->PlayerTalkClass->SendQuestGiverStatus(questStatus, guid); +} + +void WorldSession::HandleQuestgiverHelloOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid; + recv_data >> guid; + + sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_HELLO npc = %u",guid ); + + Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_NONE); + if (!pCreature) + { + sLog.outDebug( "WORLD: HandleQuestgiverHelloOpcode - 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); + // Stop the npc if moving + pCreature->StopMoving(); + + if(Script->GossipHello( _player, pCreature ) ) + return; + + pCreature->prepareGossipMenu(_player,0); + pCreature->sendPreparedGossip( _player ); +} + +void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint64 guid; + uint32 quest; + recv_data >> guid >> quest; + + if(!GetPlayer()->isAlive()) + return; + + sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_ACCEPT_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid)),quest ); + + Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT|TYPEMASK_ITEM|TYPEMASK_PLAYER); + + // no or incorrect quest giver + if(!pObject + || (pObject->GetTypeId()!=TYPEID_PLAYER && !pObject->hasQuest(quest)) + || (pObject->GetTypeId()==TYPEID_PLAYER && !((Player*)pObject)->CanShareQuest(quest)) + ) + { + _player->PlayerTalkClass->CloseGossip(); + _player->SetDivider( 0 ); + return; + } + + Quest const* qInfo = objmgr.GetQuestTemplate(quest); + if ( qInfo ) + { + // prevent cheating + if(!GetPlayer()->CanTakeQuest(qInfo,true) ) + { + _player->PlayerTalkClass->CloseGossip(); + _player->SetDivider( 0 ); + return; + } + + if( _player->GetDivider() != 0 ) + { + Player *pPlayer = ObjectAccessor::FindPlayer( _player->GetDivider() ); + if( pPlayer ) + { + pPlayer->SendPushToPartyResponse( _player, QUEST_PARTY_MSG_ACCEPT_QUEST ); + _player->SetDivider( 0 ); + } + } + + if( _player->CanAddQuest( qInfo, true ) ) + { + _player->AddQuest( qInfo, pObject ); + + if ( _player->CanCompleteQuest( quest ) ) + _player->CompleteQuest( quest ); + + switch(pObject->GetTypeId()) + { + case TYPEID_UNIT: + Script->QuestAccept(_player, ((Creature*)pObject), qInfo ); + break; + case TYPEID_ITEM: + case TYPEID_CONTAINER: + { + Script->ItemQuestAccept(_player, ((Item*)pObject), qInfo ); + + // destroy not required for quest finish quest starting item + bool destroyItem = true; + for(int i = 0; i < QUEST_OBJECTIVES_COUNT; i++) + { + if ((qInfo->ReqItemId[i] == ((Item*)pObject)->GetEntry()) && (((Item*)pObject)->GetProto()->MaxCount != 0)) + { + destroyItem = false; + break; + } + } + + if(destroyItem) + _player->DestroyItem(((Item*)pObject)->GetBagSlot(),((Item*)pObject)->GetSlot(),true); + + break; + } + case TYPEID_GAMEOBJECT: + Script->GOQuestAccept(_player, ((GameObject*)pObject), qInfo ); + break; + } + _player->PlayerTalkClass->CloseGossip(); + + if( qInfo->GetSrcSpell() > 0 ) + _player->CastSpell( _player, qInfo->GetSrcSpell(), true); + + return; + } + } + + _player->PlayerTalkClass->CloseGossip(); +} + +void WorldSession::HandleQuestgiverQuestQueryOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint64 guid; + uint32 quest; + recv_data >> guid >> quest; + sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUERY_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid)),quest ); + + // Verify that the guid is valid and is a questgiver or involved in the requested quest + Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT|TYPEMASK_ITEM); + if(!pObject||!pObject->hasQuest(quest) && !pObject->hasInvolvedQuest(quest)) + { + _player->PlayerTalkClass->CloseGossip(); + return; + } + + Quest const* pQuest = objmgr.GetQuestTemplate(quest); + if ( pQuest ) + { + _player->PlayerTalkClass->SendQuestGiverQuestDetails(pQuest, pObject->GetGUID(), true); + } +} + +void WorldSession::HandleQuestQueryOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4); + + uint32 quest; + recv_data >> quest; + sLog.outDebug( "WORLD: Received CMSG_QUEST_QUERY quest = %u",quest ); + + Quest const *pQuest = objmgr.GetQuestTemplate(quest); + if ( pQuest ) + { + _player->PlayerTalkClass->SendQuestQueryResponse( pQuest ); + } +} + +void WorldSession::HandleQuestgiverChooseRewardOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4+4); + + uint32 quest, reward; + uint64 guid; + recv_data >> guid >> quest >> reward; + + if(reward >= QUEST_REWARD_CHOICES_COUNT) + { + sLog.outError("Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player %s (guid %d) tried to get invalid reward (%u) (probably packet hacking)", _player->GetName(), _player->GetGUIDLow(), reward); + return; + } + + if(!GetPlayer()->isAlive()) + return; + + sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_CHOOSE_REWARD npc = %u, quest = %u, reward = %u",uint32(GUID_LOPART(guid)),quest,reward ); + + Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); + if(!pObject) + return; + + if(!pObject->hasInvolvedQuest(quest)) + return; + + Quest const *pQuest = objmgr.GetQuestTemplate(quest); + if( pQuest ) + { + if( _player->CanRewardQuest( pQuest, reward, true ) ) + { + _player->RewardQuest( pQuest, reward, pObject ); + + switch(pObject->GetTypeId()) + { + case TYPEID_UNIT: + if( !(Script->ChooseReward( _player, ((Creature*)pObject), pQuest, reward )) ) + { + // Send next quest + if(Quest const* nextquest = _player->GetNextQuest( guid ,pQuest ) ) + _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextquest,guid,true); + } + break; + case TYPEID_GAMEOBJECT: + if( !Script->GOChooseReward( _player, ((GameObject*)pObject), pQuest, reward ) ) + { + // Send next quest + if(Quest const* nextquest = _player->GetNextQuest( guid ,pQuest ) ) + _player->PlayerTalkClass->SendQuestGiverQuestDetails(nextquest,guid,true); + } + break; + } + } + else + _player->PlayerTalkClass->SendQuestGiverOfferReward( pQuest, guid, true ); + } +} + +void WorldSession::HandleQuestgiverRequestRewardOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint32 quest; + uint64 guid; + recv_data >> guid >> quest; + + if(!GetPlayer()->isAlive()) + return; + + sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_REQUEST_REWARD npc = %u, quest = %u",uint32(GUID_LOPART(guid)),quest ); + + Object* pObject = ObjectAccessor::GetObjectByTypeMask(*_player, guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); + if(!pObject||!pObject->hasInvolvedQuest(quest)) + return; + + if ( _player->CanCompleteQuest( quest ) ) + _player->CompleteQuest( quest ); + + if( _player->GetQuestStatus( quest ) != QUEST_STATUS_COMPLETE ) + return; + + if(Quest const *pQuest = objmgr.GetQuestTemplate(quest)) + _player->PlayerTalkClass->SendQuestGiverOfferReward( pQuest, guid, true ); +} + +void WorldSession::HandleQuestgiverCancel(WorldPacket& /*recv_data*/ ) +{ + sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_CANCEL" ); + + _player->PlayerTalkClass->CloseGossip(); +} + +void WorldSession::HandleQuestLogSwapQuest(WorldPacket& recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,1+1); + + uint8 slot1, slot2; + recv_data >> slot1 >> slot2; + + if(slot1 == slot2 || slot1 >= MAX_QUEST_LOG_SIZE || slot2 >= MAX_QUEST_LOG_SIZE) + return; + + sLog.outDebug( "WORLD: Received CMSG_QUESTLOG_SWAP_QUEST slot 1 = %u, slot 2 = %u", slot1, slot2 ); + + GetPlayer()->SwapQuestSlot(slot1,slot2); +} + +void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data,1); + + uint8 slot; + recv_data >> slot; + + sLog.outDebug( "WORLD: Received CMSG_QUESTLOG_REMOVE_QUEST slot = %u",slot ); + + if( slot < MAX_QUEST_LOG_SIZE ) + { + if(uint32 quest = _player->GetQuestSlotQuestId(slot)) + { + if(!_player->TakeQuestSourceItem( quest, true )) + return; // can't un-equip some items, reject quest cancel + + _player->SetQuestStatus( quest, QUEST_STATUS_NONE); + } + + _player->SetQuestSlot(slot, 0); + } +} + +void WorldSession::HandleQuestConfirmAccept(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data,4); + + uint32 quest; + recv_data >> quest; + + sLog.outDebug( "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = %u",quest ); +} + +void WorldSession::HandleQuestComplete(WorldPacket& recv_data) +{ + CHECK_PACKET_SIZE(recv_data,8+4); + + uint32 quest; + uint64 guid; + recv_data >> guid >> quest; + + if(!GetPlayer()->isAlive()) + return; + + sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %u, quest = %u",uint32(GUID_LOPART(guid)),quest ); + + Quest const *pQuest = objmgr.GetQuestTemplate(quest); + if( pQuest ) + { + if( _player->GetQuestStatus( quest ) != QUEST_STATUS_COMPLETE ) + { + if( pQuest->IsRepeatable() ) + _player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanCompleteRepeatableQuest(pQuest), false); + else + _player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanRewardQuest(pQuest,false), false); + } + else + _player->PlayerTalkClass->SendQuestGiverRequestItems(pQuest, guid, _player->CanRewardQuest(pQuest,false), false); + } +} + +void WorldSession::HandleQuestAutoLaunch(WorldPacket& /*recvPacket*/) +{ + sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUEST_AUTOLAUNCH (Send your log to anakin if you see this message)" ); +} + +void WorldSession::HandleQuestPushToParty(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,4); + + uint32 quest; + recvPacket >> quest; + + sLog.outDebug( "WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", quest ); + + Quest const *pQuest = objmgr.GetQuestTemplate(quest); + if( pQuest ) + { + if( _player->GetGroup() ) + { + Group *pGroup = _player->GetGroup(); + if( pGroup ) + { + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player *pPlayer = itr->getSource(); + if (!pPlayer || pPlayer == _player) // skip self + continue; + + _player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_SHARING_QUEST); + + if( _player->GetDistance( pPlayer ) > 10 ) + { + _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_TOO_FAR ); + continue; + } + + if( !pPlayer->SatisfyQuestStatus( pQuest, false ) ) + { + _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_HAVE_QUEST ); + continue; + } + + if( pPlayer->GetQuestStatus( quest ) == QUEST_STATUS_COMPLETE ) + { + _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_FINISH_QUEST ); + continue; + } + + if( !pPlayer->CanTakeQuest( pQuest, false ) ) + { + _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_CANT_TAKE_QUEST ); + continue; + } + + if( !pPlayer->SatisfyQuestLog( false ) ) + { + _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_LOG_FULL ); + continue; + } + + if( pPlayer->GetDivider() != 0 ) + { + _player->SendPushToPartyResponse( pPlayer, QUEST_PARTY_MSG_BUSY ); + continue; + } + + pPlayer->PlayerTalkClass->SendQuestGiverQuestDetails( pQuest, _player->GetGUID(), true ); + pPlayer->SetDivider( _player->GetGUID() ); + } + } + } + } +} + +void WorldSession::HandleQuestPushResult(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,8+1); + + uint64 guid; + uint8 msg; + recvPacket >> guid >> msg; + + sLog.outDebug( "WORLD: Received MSG_QUEST_PUSH_RESULT" ); + + if( _player->GetDivider() != 0 ) + { + Player *pPlayer = ObjectAccessor::FindPlayer( _player->GetDivider() ); + if( pPlayer ) + { + WorldPacket data( MSG_QUEST_PUSH_RESULT, (8+1) ); + data << uint64(guid); + data << uint8(msg); // valid values: 0-8 + pPlayer->GetSession()->SendPacket(&data); + _player->SetDivider( 0 ); + } + } +} + +uint32 WorldSession::getDialogStatus(Player *pPlayer, Object* questgiver, uint32 defstatus) +{ + uint32 result = defstatus; + + QuestRelations const* qir; + QuestRelations const* qr; + + switch(questgiver->GetTypeId()) + { + case TYPEID_GAMEOBJECT: + { + qir = &objmgr.mGOQuestInvolvedRelations; + qr = &objmgr.mGOQuestRelations; + break; + } + case TYPEID_UNIT: + { + qir = &objmgr.mCreatureQuestInvolvedRelations; + qr = &objmgr.mCreatureQuestRelations; + break; + } + default: + //its imposible, but check ^) + sLog.outError("Warning: GetDialogStatus called for unexpected type %u", questgiver->GetTypeId()); + return DIALOG_STATUS_NONE; + } + + for(QuestRelations::const_iterator i = qir->lower_bound(questgiver->GetEntry()); i != qir->upper_bound(questgiver->GetEntry()); ++i ) + { + uint32 result2 = 0; + uint32 quest_id = i->second; + Quest const *pQuest = objmgr.GetQuestTemplate(quest_id); + if ( !pQuest ) continue; + + QuestStatus status = pPlayer->GetQuestStatus( quest_id ); + if( (status == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(quest_id)) || + (pQuest->IsAutoComplete() && pPlayer->CanTakeQuest(pQuest, false)) ) + { + if ( pQuest->IsAutoComplete() && pQuest->IsRepeatable() ) + result2 = DIALOG_STATUS_REWARD_REP; + else + result2 = DIALOG_STATUS_REWARD; + } + else if ( status == QUEST_STATUS_INCOMPLETE ) + result2 = DIALOG_STATUS_INCOMPLETE; + + if (result2 > result) + result = result2; + } + + for(QuestRelations::const_iterator i = qr->lower_bound(questgiver->GetEntry()); i != qr->upper_bound(questgiver->GetEntry()); ++i ) + { + uint32 result2 = 0; + uint32 quest_id = i->second; + Quest const *pQuest = objmgr.GetQuestTemplate(quest_id); + if ( !pQuest ) + continue; + + QuestStatus status = pPlayer->GetQuestStatus( quest_id ); + if ( status == QUEST_STATUS_NONE ) + { + if ( pPlayer->CanSeeStartQuest( pQuest ) ) + { + if ( pPlayer->SatisfyQuestLevel(pQuest, false) ) + { + if ( pQuest->IsAutoComplete() || (pQuest->IsRepeatable() && pPlayer->getQuestStatusMap()[quest_id].m_rewarded)) + result2 = DIALOG_STATUS_REWARD_REP; + else if (pPlayer->getLevel() <= pQuest->GetQuestLevel() + sWorld.getConfig(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF) ) + { + if (pQuest->HasFlag(QUEST_FLAGS_DAILY)) + result2 = DIALOG_STATUS_AVAILABLE_REP; + else + result2 = DIALOG_STATUS_AVAILABLE; + } + else + result2 = DIALOG_STATUS_CHAT; + } + else + result2 = DIALOG_STATUS_UNAVAILABLE; + } + } + + if (result2 > result) + result = result2; + } + + if(questgiver->GetTypeId()==TYPEID_UNIT && ((Creature*)questgiver)->isCanTrainingAndResetTalentsOf(pPlayer) && result < DIALOG_STATUS_CHAT) + result = DIALOG_STATUS_CHAT; + + return result; +} + +void WorldSession::HandleQuestgiverStatusQueryMultipleOpcode(WorldPacket& /*recvPacket*/) +{ + sLog.outDebug("WORLD: Received CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY"); + + uint32 count = 0; + + WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4); + data << uint32(count); // placeholder + + for(Player::ClientGUIDs::iterator itr = _player->m_clientGUIDs.begin(); itr != _player->m_clientGUIDs.end(); ++itr) + { + uint8 questStatus = DIALOG_STATUS_NONE; + uint8 defstatus = DIALOG_STATUS_NONE; + + if(IS_CREATURE_GUID(*itr)) + { + Creature *questgiver = ObjectAccessor::GetCreature(*_player, *itr); + if(!questgiver || questgiver->IsHostileTo(_player)) + continue; + if(!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER)) + continue; + questStatus = Script->NPCDialogStatus(_player, questgiver); + if( questStatus > 6 ) + questStatus = getDialogStatus(_player, questgiver, defstatus); + + data << uint64(questgiver->GetGUID()); + data << uint8(questStatus); + ++count; + } + else if(IS_GAMEOBJECT_GUID(*itr)) + { + GameObject *questgiver = ObjectAccessor::GetGameObject(*_player, *itr); + if(!questgiver) + continue; + if(questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER) + continue; + questStatus = Script->GODialogStatus(_player, questgiver); + if( questStatus > 6 ) + questStatus = getDialogStatus(_player, questgiver, defstatus); + + data << uint64(questgiver->GetGUID()); + data << uint8(questStatus); + ++count; + } + } + + data.put(0, count); // write real count + SendPacket(&data); +} diff --git a/src/game/RandomMovementGenerator.cpp b/src/game/RandomMovementGenerator.cpp new file mode 100644 index 00000000000..1022a0699f6 --- /dev/null +++ b/src/game/RandomMovementGenerator.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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::_setRandomLocation(Creature &creature) +{ + float X,Y,Z,z,nx,ny,nz,wander_distance,ori,dist; + + creature.GetRespawnCoord(X, Y, Z); + 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; + dist = distanceX*distanceX + distanceY*distanceY; + + 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 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.SetUnitMovementFlags(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::Initialize(Creature &creature) +{ + if(!creature.isAlive()) + return; + + if (creature.canFly()) + creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2); + else + creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE ); + _setRandomLocation(creature); +} + +template<> +void +RandomMovementGenerator::Reset(Creature &creature) +{ + Initialize(creature); +} + +template<> +bool +RandomMovementGenerator::Update(Creature &creature, const uint32 &diff) +{ + if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | 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.SetUnitMovementFlags(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/RandomMovementGenerator.h b/src/game/RandomMovementGenerator.h new file mode 100644 index 00000000000..62b836e4261 --- /dev/null +++ b/src/game/RandomMovementGenerator.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_RANDOMMOTIONGENERATOR_H +#define MANGOS_RANDOMMOTIONGENERATOR_H + +#include "MovementGenerator.h" +#include "DestinationHolder.h" +#include "Traveller.h" + + +template +class MANGOS_DLL_SPEC RandomMovementGenerator +: public MovementGeneratorMedium< T, RandomMovementGenerator > +{ + public: + RandomMovementGenerator(const Unit &) : i_nextMoveTime(0) {} + + void _setRandomLocation(T &); + void Initialize(T &); + void Finalize(T &) {} + void Reset(T &); + bool Update(T &, const uint32 &); + void UpdateMapPosition(uint32 mapid, float &x ,float &y, float &z) + { + i_destinationHolder.GetLocationNow(mapid, x,y,z); + } + MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; } + private: + TimeTrackerSmall i_nextMoveTime; + + DestinationHolder< Traveller > i_destinationHolder; + uint32 i_nextMove; +}; +#endif diff --git a/src/game/ReactorAI.cpp b/src/game/ReactorAI.cpp new file mode 100644 index 00000000000..a91af776e6d --- /dev/null +++ b/src/game/ReactorAI.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ByteBuffer.h" +#include "ReactorAI.h" +#include "Errors.h" +#include "Creature.h" +#include "Log.h" +#include "ObjectAccessor.h" + +#define REACTOR_VISIBLE_RANGE (26.46f) + +int +ReactorAI::Permissible(const Creature *creature) +{ + if( creature->isCivilian() || creature->IsNeutralToAll() ) + return PERMIT_BASE_REACTIVE; + + return PERMIT_BASE_NO; +} + +void +ReactorAI::MoveInLineOfSight(Unit *) +{ +} + +void +ReactorAI::AttackStart(Unit *p) +{ + if(!p) + return; + + if(i_creature.Attack(p,true)) + { + DEBUG_LOG("Tag unit GUID: %u (TypeId: %u) as a victim", p->GetGUIDLow(), p->GetTypeId()); + i_creature.SetInCombatWith(p); + p->SetInCombatWith(&i_creature); + + i_creature.AddThreat(p, 0.0f); + i_victimGuid = p->GetGUID(); + i_creature.GetMotionMaster()->MoveChase(p); + } +} + +bool +ReactorAI::IsVisible(Unit *) const +{ + return false; +} + +void +ReactorAI::UpdateAI(const uint32 /*time_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(); + } + } +} + +void +ReactorAI::EnterEvadeMode() +{ + if( !i_creature.isAlive() ) + { + DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_creature.GetGUIDLow()); + i_creature.GetMotionMaster()->MovementExpired(); + i_creature.GetMotionMaster()->MoveIdle(); + 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->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 %s [guid=%u]", victim->isAlive() ? "out run him" : "is dead", i_creature.GetGUIDLow()); + } + + i_creature.RemoveAllAuras(); + i_creature.DeleteThreatList(); + i_victimGuid = 0; + i_creature.CombatStop(); + i_creature.SetLootRecipient(NULL); + + // Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead + if( i_creature.GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE ) + i_creature.GetMotionMaster()->MoveTargetedHome(); +} diff --git a/src/game/ReactorAI.h b/src/game/ReactorAI.h new file mode 100644 index 00000000000..a64208b89ef --- /dev/null +++ b/src/game/ReactorAI.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_REACTORAI_H +#define MANGOS_REACTORAI_H + +#include "CreatureAI.h" + +class Unit; + +class MANGOS_DLL_DECL ReactorAI : public CreatureAI +{ + public: + + ReactorAI(Creature &c) : i_creature(c), i_victimGuid(0) {} + + void MoveInLineOfSight(Unit *); + void AttackStart(Unit *); + void EnterEvadeMode(); + bool IsVisible(Unit *) const; + + void UpdateAI(const uint32); + static int Permissible(const Creature *); + + private: + Creature &i_creature; + uint64 i_victimGuid; +}; +#endif diff --git a/src/game/ScriptCalls.cpp b/src/game/ScriptCalls.cpp new file mode 100644 index 00000000000..c3021958804 --- /dev/null +++ b/src/game/ScriptCalls.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef WIN32 +#include +#endif + +#include "Platform/Define.h" +#include "ScriptCalls.h" + +ScriptsSet Script=NULL; + +void UnloadScriptingModule() +{ + if(Script) + { + //todo: some check if some func from script library is called right now + Script->ScriptsFree(); + MANGOS_CLOSE_LIBRARY(Script->hScriptsLib); + delete Script; + Script = NULL; + } +} + +bool LoadScriptingModule(char const* libName) +{ + ScriptsSet testScript=new _ScriptSet; + + std::string name = strlen(libName) ? libName : MANGOS_SCRIPT_NAME; + name += MANGOS_SCRIPT_EXT; + + testScript->hScriptsLib=MANGOS_LOAD_LIBRARY(name.c_str()); + + if(!testScript->hScriptsLib ) + { + printf("Error loading Scripts Library %s !\n",name.c_str()); + delete testScript; + return false; + } + + if( !(testScript->ScriptsInit =(scriptCallScriptsInit )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ScriptsInit" )) + ||!(testScript->ScriptsFree =(scriptCallScriptsFree )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ScriptsFree" )) + ||!(testScript->GossipHello =(scriptCallGossipHello )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GossipHello" )) + ||!(testScript->GOChooseReward =(scriptCallGOChooseReward )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GOChooseReward" )) + ||!(testScript->QuestAccept =(scriptCallQuestAccept )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"QuestAccept" )) + ||!(testScript->GossipSelect =(scriptCallGossipSelect )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GossipSelect" )) + ||!(testScript->GossipSelectWithCode=(scriptCallGossipSelectWithCode)MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GossipSelectWithCode")) + ||!(testScript->QuestSelect =(scriptCallQuestSelect )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"QuestSelect" )) + ||!(testScript->QuestComplete =(scriptCallQuestComplete )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"QuestComplete" )) + ||!(testScript->NPCDialogStatus =(scriptCallNPCDialogStatus )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"NPCDialogStatus" )) + ||!(testScript->GODialogStatus =(scriptCallGODialogStatus )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GODialogStatus" )) + ||!(testScript->ChooseReward =(scriptCallChooseReward )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ChooseReward" )) + ||!(testScript->ItemHello =(scriptCallItemHello )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ItemHello" )) + ||!(testScript->GOHello =(scriptCallGOHello )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GOHello" )) + ||!(testScript->scriptAreaTrigger =(scriptCallAreaTrigger )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"AreaTrigger" )) + ||!(testScript->ItemQuestAccept =(scriptCallItemQuestAccept )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ItemQuestAccept" )) + ||!(testScript->GOQuestAccept =(scriptCallGOQuestAccept )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GOQuestAccept" )) + ||!(testScript->ReceiveEmote =(scriptCallReceiveEmote )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ReceiveEmote" )) + ||!(testScript->ItemUse =(scriptCallItemUse )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ItemUse" )) + ||!(testScript->GetAI =(scriptCallGetAI )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GetAI" )) + ||!(testScript->CreateInstanceData =(scriptCallCreateInstanceData )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"CreateInstanceData" )) + ) + { + printf("Error loading Scripts Library %s !\n Library missing required functions.",name.c_str()); + MANGOS_CLOSE_LIBRARY(testScript->hScriptsLib); + delete testScript; + return false; + } + + printf("Scripts Library %s was successfully loaded.\n",name.c_str()); + + //heh we are still there :P we have a valid library + //we reload script + UnloadScriptingModule(); + + Script=testScript; + Script->ScriptsInit(); + + return true; +} diff --git a/src/game/ScriptCalls.h b/src/game/ScriptCalls.h new file mode 100644 index 00000000000..07c415497ad --- /dev/null +++ b/src/game/ScriptCalls.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SCRIPT_CALLS_H +#define __SCRIPT_CALLS_H + +#include "Common.h" +#include "ObjectMgr.h" + +class Creature; +class CreatureAI; +class GameObject; +class Item; +class Player; +class Quest; +class SpellCastTargets; +class Map; +class InstanceData; + +bool LoadScriptingModule(char const* libName = ""); +void UnloadScriptingModule(); + +typedef void(MANGOS_IMPORT * scriptCallScriptsInit) (); +typedef void(MANGOS_IMPORT * scriptCallScriptsFree) (); + +typedef bool(MANGOS_IMPORT * scriptCallGossipHello) (Player *player, Creature *_Creature ); +typedef bool(MANGOS_IMPORT * scriptCallQuestAccept) (Player *player, Creature *_Creature, Quest const *); +typedef bool(MANGOS_IMPORT * scriptCallGossipSelect)(Player *player, Creature *_Creature, uint32 sender, uint32 action); +typedef bool(MANGOS_IMPORT * scriptCallGossipSelectWithCode)( Player *player, Creature *_Creature, uint32 sender, uint32 action, const char* sCode ); +typedef bool(MANGOS_IMPORT * scriptCallQuestSelect)( Player *player, Creature *_Creature, Quest const* ); +typedef bool(MANGOS_IMPORT * scriptCallQuestComplete)(Player *player, Creature *_Creature, Quest const*); +typedef uint32(MANGOS_IMPORT * scriptCallNPCDialogStatus)( Player *player, Creature *_Creature); +typedef uint32(MANGOS_IMPORT * scriptCallGODialogStatus)( Player *player, GameObject * _GO); +typedef bool(MANGOS_IMPORT * scriptCallChooseReward)( Player *player, Creature *_Creature, Quest const*, uint32 opt ); +typedef bool(MANGOS_IMPORT * scriptCallItemHello)( Player *player, Item *, Quest const*); +typedef bool(MANGOS_IMPORT * scriptCallGOHello)( Player *player, GameObject * ); +typedef bool(MANGOS_IMPORT * scriptCallAreaTrigger)( Player *player, AreaTriggerEntry const* ); +typedef bool(MANGOS_IMPORT * scriptCallItemQuestAccept)(Player *player, Item *, Quest const*); +typedef bool(MANGOS_IMPORT * scriptCallGOQuestAccept)(Player *player, GameObject *, Quest const*); +typedef bool(MANGOS_IMPORT * scriptCallGOChooseReward)(Player *player, GameObject *, Quest const*, uint32 opt ); +typedef bool(MANGOS_IMPORT * scriptCallReceiveEmote) ( Player *player, Creature *_Creature, uint32 emote ); +typedef bool(MANGOS_IMPORT * scriptCallItemUse) (Player *player, Item *_Item, SpellCastTargets const& targets); +typedef CreatureAI* (MANGOS_IMPORT * scriptCallGetAI) ( Creature *_Creature ); +typedef InstanceData* (MANGOS_IMPORT * scriptCallCreateInstanceData) (Map *map); + +typedef struct +{ + scriptCallScriptsInit ScriptsInit; + scriptCallScriptsFree ScriptsFree; + + scriptCallGossipHello GossipHello; + scriptCallGOChooseReward GOChooseReward; + scriptCallQuestAccept QuestAccept; + scriptCallGossipSelect GossipSelect; + scriptCallGossipSelectWithCode GossipSelectWithCode; + scriptCallQuestSelect QuestSelect; + scriptCallQuestComplete QuestComplete; + scriptCallNPCDialogStatus NPCDialogStatus; + scriptCallGODialogStatus GODialogStatus; + scriptCallChooseReward ChooseReward; + scriptCallItemHello ItemHello; + scriptCallGOHello GOHello; + scriptCallAreaTrigger scriptAreaTrigger; + scriptCallItemQuestAccept ItemQuestAccept; + scriptCallGOQuestAccept GOQuestAccept; + scriptCallReceiveEmote ReceiveEmote; + scriptCallItemUse ItemUse; + scriptCallGetAI GetAI; + scriptCallCreateInstanceData CreateInstanceData; + + MANGOS_LIBRARY_HANDLE hScriptsLib; +}_ScriptSet,*ScriptsSet; + +extern ScriptsSet Script; +#endif diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h new file mode 100644 index 00000000000..06c6daead2a --- /dev/null +++ b/src/game/SharedDefines.h @@ -0,0 +1,1997 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_SHAREDDEFINES_H +#define MANGOS_SHAREDDEFINES_H + +#include "Platform/Define.h" +#include + +enum Gender +{ + GENDER_MALE = 0, + GENDER_FEMALE = 1, + GENDER_NONE = 2 +}; + +// Race value is index in ChrRaces.dbc +enum Races +{ + RACE_HUMAN = 1, + RACE_ORC = 2, + RACE_DWARF = 3, + RACE_NIGHTELF = 4, + RACE_UNDEAD_PLAYER = 5, + RACE_TAUREN = 6, + RACE_GNOME = 7, + RACE_TROLL = 8, + RACE_GOBLIN = 9, + RACE_BLOODELF = 10, + RACE_DRAENEI = 11, + RACE_FEL_ORC = 12, + RACE_NAGA = 13, + RACE_BROKEN = 14, + RACE_SKELETON = 15, + MAX_RACES = 16 +}; + +#define RACEMASK_ALL_PLAYABLE \ + ((1<<(RACE_HUMAN-1)) |(1<<(RACE_ORC-1)) |(1<<(RACE_DWARF-1)) | \ + (1<<(RACE_NIGHTELF-1))|(1<<(RACE_UNDEAD_PLAYER-1))|(1<<(RACE_TAUREN-1)) | \ + (1<<(RACE_GNOME-1)) |(1<<(RACE_TROLL-1)) |(1<<(RACE_BLOODELF-1))| \ + (1<<(RACE_DRAENEI-1)) ) + +// Class value is index in ChrClasses.dbc +enum Classes +{ + CLASS_WARRIOR = 1, + CLASS_PALADIN = 2, + CLASS_HUNTER = 3, + CLASS_ROGUE = 4, + CLASS_PRIEST = 5, + // CLASS_UNK1 = 6, DK + CLASS_SHAMAN = 7, + CLASS_MAGE = 8, + CLASS_WARLOCK = 9, + // CLASS_UNK2 = 10,unused + CLASS_DRUID = 11, + MAX_CLASSES = 12 +}; + +#define CLASSMASK_ALL_PLAYABLE \ + ((1<<(CLASS_WARRIOR-1))|(1<<(CLASS_PALADIN-1))|(1<<(CLASS_HUNTER-1))| \ + (1<<(CLASS_ROGUE-1)) |(1<<(CLASS_PRIEST-1)) |(1<<(CLASS_SHAMAN-1))| \ + (1<<(CLASS_MAGE-1)) |(1<<(CLASS_WARLOCK-1))|(1<<(CLASS_DRUID-1)) ) + +#define CLASSMASK_WAND_USERS ((1<<(CLASS_PRIEST-1))|(1<<(CLASS_MAGE-1))|(1<<(CLASS_WARLOCK-1))) + +#define PLAYER_MAX_BATTLEGROUND_QUEUES 3 + +enum ReputationRank +{ + REP_HATED = 0, + REP_HOSTILE = 1, + REP_UNFRIENDLY = 2, + REP_NEUTRAL = 3, + REP_FRIENDLY = 4, + REP_HONORED = 5, + REP_REVERED = 6, + REP_EXALTED = 7 +}; + +#define MIN_REPUTATION_RANK (REP_HATED) +#define MAX_REPUTATION_RANK 8 + +enum MoneyConstants +{ + COPPER = 1, + SILVER = COPPER*100, + GOLD = SILVER*100 +}; + +enum Stats +{ + STAT_STRENGTH = 0, + STAT_AGILITY = 1, + STAT_STAMINA = 2, + STAT_INTELLECT = 3, + STAT_SPIRIT = 4 +}; + +#define MAX_STATS 5 + +enum Powers +{ + POWER_MANA = 0, + POWER_RAGE = 1, + POWER_FOCUS = 2, + POWER_ENERGY = 3, + POWER_HAPPINESS = 4, + POWER_RUNES = 5, + POWER_HEALTH = 0xFFFFFFFE // (-2 as signed value) +}; + +#define MAX_POWERS 5 // not count POWER_RUNES for now + +enum SpellSchools +{ + SPELL_SCHOOL_NORMAL = 0, + SPELL_SCHOOL_HOLY = 1, + SPELL_SCHOOL_FIRE = 2, + SPELL_SCHOOL_NATURE = 3, + SPELL_SCHOOL_FROST = 4, + SPELL_SCHOOL_SHADOW = 5, + SPELL_SCHOOL_ARCANE = 6 +}; + +#define MAX_SPELL_SCHOOL 7 + +enum SpellSchoolMask +{ + SPELL_SCHOOL_MASK_NONE = 0x00, // not exist + SPELL_SCHOOL_MASK_NORMAL = (1 << SPELL_SCHOOL_NORMAL), // PHYSICAL (Armor) + SPELL_SCHOOL_MASK_HOLY = (1 << SPELL_SCHOOL_HOLY ), + SPELL_SCHOOL_MASK_FIRE = (1 << SPELL_SCHOOL_FIRE ), + SPELL_SCHOOL_MASK_NATURE = (1 << SPELL_SCHOOL_NATURE), + SPELL_SCHOOL_MASK_FROST = (1 << SPELL_SCHOOL_FROST ), + SPELL_SCHOOL_MASK_SHADOW = (1 << SPELL_SCHOOL_SHADOW), + SPELL_SCHOOL_MASK_ARCANE = (1 << SPELL_SCHOOL_ARCANE), + + // unions + + // 124, not include normal and holy damage + SPELL_SCHOOL_MASK_SPELL = ( SPELL_SCHOOL_MASK_FIRE | + SPELL_SCHOOL_MASK_NATURE | SPELL_SCHOOL_MASK_FROST | + SPELL_SCHOOL_MASK_SHADOW | SPELL_SCHOOL_MASK_ARCANE ), + // 126 + SPELL_SCHOOL_MASK_MAGIC = ( SPELL_SCHOOL_MASK_HOLY | SPELL_SCHOOL_MASK_SPELL ), + + // 127 + SPELL_SCHOOL_MASK_ALL = ( SPELL_SCHOOL_MASK_NORMAL | SPELL_SCHOOL_MASK_MAGIC ) +}; + +#define SPELL_SCHOOL_MASK_MAGIC \ + ( SPELL_SCHOOL_MASK_HOLY | SPELL_SCHOOL_MASK_FIRE | SPELL_SCHOOL_MASK_NATURE | \ + SPELL_SCHOOL_MASK_FROST | SPELL_SCHOOL_MASK_SHADOW | \ + SPELL_SCHOOL_MASK_ARCANE ) + +inline SpellSchools GetFirstSchoolInMask(SpellSchoolMask mask) +{ + for(int i = 0; i < MAX_SPELL_SCHOOL; ++i) + if(mask & (1 << i)) + return SpellSchools(i); + + return SPELL_SCHOOL_NORMAL; +} + +enum ItemQualities +{ + ITEM_QUALITY_POOR = 0, //GREY + ITEM_QUALITY_NORMAL = 1, //WHITE + ITEM_QUALITY_UNCOMMON = 2, //GREEN + ITEM_QUALITY_RARE = 3, //BLUE + ITEM_QUALITY_EPIC = 4, //PURPLE + ITEM_QUALITY_LEGENDARY = 5, //ORANGE + ITEM_QUALITY_ARTIFACT = 6 //LIGHT YELLOW +}; + +#define MAX_ITEM_QUALITY 7 + +// *********************************** +// Spell Attributes definitions +// *********************************** + +#define SPELL_ATTR_UNK0 0x00000001 // 0 +#define SPELL_ATTR_RANGED 0x00000002 // 1 All ranged abilites have this flag +#define SPELL_ATTR_ON_NEXT_SWING_1 0x00000004 // 2 on next swing +#define SPELL_ATTR_UNK3 0x00000008 // 3 not set in 2.4.2 +#define SPELL_ATTR_UNK4 0x00000010 // 4 +#define SPELL_ATTR_UNK5 0x00000020 // 5 trade spells? +#define SPELL_ATTR_PASSIVE 0x00000040 // 6 Passive spell +#define SPELL_ATTR_UNK7 0x00000080 // 7 visible? +#define SPELL_ATTR_UNK8 0x00000100 // 8 +#define SPELL_ATTR_UNK9 0x00000200 // 9 +#define SPELL_ATTR_ON_NEXT_SWING_2 0x00000400 // 10 on next swing 2 +#define SPELL_ATTR_UNK11 0x00000800 // 11 +#define SPELL_ATTR_DAYTIME_ONLY 0x00001000 // 12 only useable at daytime, not set in 2.4.2 +#define SPELL_ATTR_NIGHT_ONLY 0x00002000 // 13 only useable at night, not set in 2.4.2 +#define SPELL_ATTR_INDOORS_ONLY 0x00004000 // 14 only useable indoors, not set in 2.4.2 +#define SPELL_ATTR_OUTDOORS_ONLY 0x00008000 // 15 Only useable outdoors. +#define SPELL_ATTR_NOT_SHAPESHIFT 0x00010000 // 16 Not while shapeshifted +#define SPELL_ATTR_ONLY_STEALTHED 0x00020000 // 17 Must be in stealth +#define SPELL_ATTR_UNK18 0x00040000 // 18 +#define SPELL_ATTR_LEVEL_DAMAGE_CALCULATION 0x00080000 // 19 spelldamage depends on caster level +#define SPELL_ATTR_STOP_ATTACK_TARGET 0x00100000 // 20 Stop attack after use this spell (and not begin attack if use) +#define SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK 0x00200000 // 21 Cannot be dodged/parried/blocked +#define SPELL_ATTR_UNK22 0x00400000 // 22 +#define SPELL_ATTR_UNK23 0x00800000 // 23 castable while dead? +#define SPELL_ATTR_CASTABLE_WHILE_MOUNTED 0x01000000 // 24 castable while mounted +#define SPELL_ATTR_DISABLED_WHILE_ACTIVE 0x02000000 // 25 Activate and start cooldown after aura fade or remove summoned creature or go +#define SPELL_ATTR_UNK26 0x04000000 // 26 +#define SPELL_ATTR_CASTABLE_WHILE_SITTING 0x08000000 // 27 castable while sitting +#define SPELL_ATTR_CANT_USED_IN_COMBAT 0x10000000 // 28 Cannot be used in combat +#define SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY 0x20000000 // 29 unaffected by invulnerability (hmm possible not...) +#define SPELL_ATTR_UNK30 0x40000000 // 30 breakable by damage? +#define SPELL_ATTR_CANT_CANCEL 0x80000000 // 31 positive aura can't be canceled + +#define SPELL_ATTR_EX_UNK0 0x00000001 // 0 +#define SPELL_ATTR_EX_DRAIN_ALL_POWER 0x00000002 // 1 use all power (Only paladin Lay of Hands and Bunyanize) +#define SPELL_ATTR_EX_CHANNELED_1 0x00000004 // 2 channeled 1 +#define SPELL_ATTR_EX_UNK3 0x00000008 // 3 +#define SPELL_ATTR_EX_UNK4 0x00000010 // 4 +#define SPELL_ATTR_EX_NOT_BREAK_STEALTH 0x00000020 // 5 Not break stealth +#define SPELL_ATTR_EX_CHANNELED_2 0x00000040 // 6 channeled 2 +#define SPELL_ATTR_EX_NEGATIVE 0x00000080 // 7 +#define SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET 0x00000100 // 8 Spell req target not to be in combat state +#define SPELL_ATTR_EX_UNK9 0x00000200 // 9 +#define SPELL_ATTR_EX_UNK10 0x00000400 // 10 +#define SPELL_ATTR_EX_UNK11 0x00000800 // 11 +#define SPELL_ATTR_EX_UNK12 0x00001000 // 12 +#define SPELL_ATTR_EX_UNK13 0x00002000 // 13 +#define SPELL_ATTR_EX_UNK14 0x00004000 // 14 +#define SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY 0x00008000 // 15 remove auras on immunity +#define SPELL_ATTR_EX_UNAFFECTED_BY_SCHOOL_IMMUNE 0x00010000 // 16 unaffected by school immunity +#define SPELL_ATTR_EX_UNK17 0x00020000 // 17 +#define SPELL_ATTR_EX_UNK18 0x00040000 // 18 +#define SPELL_ATTR_EX_UNK19 0x00080000 // 19 +#define SPELL_ATTR_EX_REQ_COMBO_POINTS1 0x00100000 // 20 Req combo points on target +#define SPELL_ATTR_EX_UNK21 0x00200000 // 21 +#define SPELL_ATTR_EX_REQ_COMBO_POINTS2 0x00400000 // 22 Req combo points on target +#define SPELL_ATTR_EX_UNK23 0x00800000 // 23 +#define SPELL_ATTR_EX_UNK24 0x01000000 // 24 Req fishing pole?? +#define SPELL_ATTR_EX_UNK25 0x02000000 // 25 not set in 2.4.2 +#define SPELL_ATTR_EX_UNK26 0x04000000 // 26 +#define SPELL_ATTR_EX_UNK27 0x08000000 // 27 +#define SPELL_ATTR_EX_UNK28 0x10000000 // 28 +#define SPELL_ATTR_EX_UNK29 0x20000000 // 29 +#define SPELL_ATTR_EX_UNK30 0x40000000 // 30 overpower +#define SPELL_ATTR_EX_UNK31 0x80000000 // 31 + +#define SPELL_ATTR_EX2_UNK0 0x00000001 // 0 +#define SPELL_ATTR_EX2_UNK1 0x00000002 // 1 +#define SPELL_ATTR_EX2_UNK2 0x00000004 // 2 +#define SPELL_ATTR_EX2_UNK3 0x00000008 // 3 +#define SPELL_ATTR_EX2_UNK4 0x00000010 // 4 +#define SPELL_ATTR_EX2_UNK5 0x00000020 // 5 +#define SPELL_ATTR_EX2_UNK6 0x00000040 // 6 +#define SPELL_ATTR_EX2_UNK7 0x00000080 // 7 +#define SPELL_ATTR_EX2_UNK8 0x00000100 // 8 not set in 2.4.2 +#define SPELL_ATTR_EX2_UNK9 0x00000200 // 9 +#define SPELL_ATTR_EX2_UNK10 0x00000400 // 10 +#define SPELL_ATTR_EX2_HEALTH_FUNNEL 0x00000800 // 11 +#define SPELL_ATTR_EX2_UNK12 0x00001000 // 12 +#define SPELL_ATTR_EX2_UNK13 0x00002000 // 13 +#define SPELL_ATTR_EX2_UNK14 0x00004000 // 14 +#define SPELL_ATTR_EX2_UNK15 0x00008000 // 15 not set in 2.4.2 +#define SPELL_ATTR_EX2_UNK16 0x00010000 // 16 +#define SPELL_ATTR_EX2_UNK17 0x00020000 // 17 Hunters Shot and Stings only have this flag +#define SPELL_ATTR_EX2_UNK18 0x00040000 // 18 Only Revive pet - possible req dead pet +#define SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT 0x00080000 // 19 does not necessarly need shapeshift +#define SPELL_ATTR_EX2_UNK20 0x00100000 // 20 +#define SPELL_ATTR_EX2_UNK21 0x00200000 // 21 +#define SPELL_ATTR_EX2_UNK22 0x00400000 // 22 +#define SPELL_ATTR_EX2_UNK23 0x00800000 // 23 Only mage Arcane Concentration have this flag +#define SPELL_ATTR_EX2_UNK24 0x01000000 // 24 +#define SPELL_ATTR_EX2_UNK25 0x02000000 // 25 +#define SPELL_ATTR_EX2_UNK26 0x04000000 // 26 unaffected by school immunity +#define SPELL_ATTR_EX2_UNK27 0x08000000 // 27 +#define SPELL_ATTR_EX2_UNK28 0x10000000 // 28 +#define SPELL_ATTR_EX2_CANT_CRIT 0x20000000 // 29 Spell can't crit +#define SPELL_ATTR_EX2_UNK30 0x40000000 // 30 +#define SPELL_ATTR_EX2_UNK31 0x80000000 // 31 + +#define SPELL_ATTR_EX3_UNK0 0x00000001 // 0 +#define SPELL_ATTR_EX3_UNK1 0x00000002 // 1 +#define SPELL_ATTR_EX3_UNK2 0x00000004 // 2 +#define SPELL_ATTR_EX3_UNK3 0x00000008 // 3 +#define SPELL_ATTR_EX3_UNK4 0x00000010 // 4 Druid Rebirth only this spell have this flag +#define SPELL_ATTR_EX3_UNK5 0x00000020 // 5 +#define SPELL_ATTR_EX3_UNK6 0x00000040 // 6 +#define SPELL_ATTR_EX3_UNK7 0x00000080 // 7 +#define SPELL_ATTR_EX3_UNK8 0x00000100 // 8 +#define SPELL_ATTR_EX3_UNK9 0x00000200 // 9 +#define SPELL_ATTR_EX3_MAIN_HAND 0x00000400 // 10 Main hand weapon required +#define SPELL_ATTR_EX3_BATTLEGROUND 0x00000800 // 11 Can casted only on battleground +#define SPELL_ATTR_EX3_UNK12 0x00001000 // 12 +#define SPELL_ATTR_EX3_UNK13 0x00002000 // 13 +#define SPELL_ATTR_EX3_UNK14 0x00004000 // 14 "Honorless Target" only this spells have this flag +#define SPELL_ATTR_EX3_UNK15 0x00008000 // 15 Auto Shoot, Shoot, Throw, - this is autoshot flag +#define SPELL_ATTR_EX3_UNK16 0x00010000 // 16 +#define SPELL_ATTR_EX3_NO_INITIAL_AGGRO 0x00020000 // 17 no initial aggro +#define SPELL_ATTR_EX3_UNK18 0x00040000 // 18 +#define SPELL_ATTR_EX3_UNK19 0x00080000 // 19 +#define SPELL_ATTR_EX3_DEATH_PERSISTENT 0x00100000 // 20 Death persistent spells +#define SPELL_ATTR_EX3_UNK21 0x00200000 // 21 +#define SPELL_ATTR_EX3_REQ_WAND 0x00400000 // 22 Req wand +#define SPELL_ATTR_EX3_UNK23 0x00800000 // 23 +#define SPELL_ATTR_EX3_REQ_OFFHAND 0x01000000 // 24 Req offhand weapon +#define SPELL_ATTR_EX3_UNK25 0x02000000 // 25 +#define SPELL_ATTR_EX3_UNK26 0x04000000 // 26 +#define SPELL_ATTR_EX3_UNK27 0x08000000 // 27 +#define SPELL_ATTR_EX3_UNK28 0x10000000 // 28 +#define SPELL_ATTR_EX3_UNK29 0x20000000 // 29 +#define SPELL_ATTR_EX3_UNK30 0x40000000 // 30 +#define SPELL_ATTR_EX3_UNK31 0x80000000 // 31 + +#define SPELL_ATTR_EX4_UNK0 0x00000001 // 0 +#define SPELL_ATTR_EX4_UNK1 0x00000002 // 1 proc on finishing move? +#define SPELL_ATTR_EX4_UNK2 0x00000004 // 2 +#define SPELL_ATTR_EX4_UNK3 0x00000008 // 3 +#define SPELL_ATTR_EX4_UNK4 0x00000010 // 4 +#define SPELL_ATTR_EX4_UNK5 0x00000020 // 5 +#define SPELL_ATTR_EX4_UNK6 0x00000040 // 6 +#define SPELL_ATTR_EX4_UNK7 0x00000080 // 7 +#define SPELL_ATTR_EX4_UNK8 0x00000100 // 8 +#define SPELL_ATTR_EX4_UNK9 0x00000200 // 9 +#define SPELL_ATTR_EX4_SPELL_VS_EXTEND_COST 0x00000400 // 10 Rogue Shiv have this flag +#define SPELL_ATTR_EX4_UNK11 0x00000800 // 11 +#define SPELL_ATTR_EX4_UNK12 0x00001000 // 12 +#define SPELL_ATTR_EX4_UNK13 0x00002000 // 13 +#define SPELL_ATTR_EX4_UNK14 0x00004000 // 14 +#define SPELL_ATTR_EX4_UNK15 0x00008000 // 15 +#define SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA 0x00010000 // 16 not usable in arena +#define SPELL_ATTR_EX4_USABLE_IN_ARENA 0x00020000 // 17 usable in arena +#define SPELL_ATTR_EX4_UNK18 0x00040000 // 18 +#define SPELL_ATTR_EX4_UNK19 0x00080000 // 19 +#define SPELL_ATTR_EX4_UNK20 0x00100000 // 20 +#define SPELL_ATTR_EX4_UNK21 0x00200000 // 21 +#define SPELL_ATTR_EX4_UNK22 0x00400000 // 22 +#define SPELL_ATTR_EX4_UNK23 0x00800000 // 23 +#define SPELL_ATTR_EX4_UNK24 0x01000000 // 24 +#define SPELL_ATTR_EX4_UNK25 0x02000000 // 25 pet scaling auras +#define SPELL_ATTR_EX4_CAST_ONLY_IN_OUTLAND 0x04000000 // 26 Can only be used in Outland. +#define SPELL_ATTR_EX4_UNK27 0x08000000 // 27 +#define SPELL_ATTR_EX4_UNK28 0x10000000 // 28 +#define SPELL_ATTR_EX4_UNK29 0x20000000 // 29 +#define SPELL_ATTR_EX4_UNK30 0x40000000 // 30 +#define SPELL_ATTR_EX4_UNK31 0x80000000 // 31 + +#define SPELL_ATTR_EX5_UNK0 0x00000001 // 0 +#define SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP 0x00000002 // 1 not need reagents if UNIT_FLAG_PREPARATION +#define SPELL_ATTR_EX5_UNK2 0x00000004 // 2 +#define SPELL_ATTR_EX5_USABLE_WHILE_STUNNED 0x00000008 // 3 usable while stunned +#define SPELL_ATTR_EX5_UNK4 0x00000010 // 4 +#define SPELL_ATTR_EX5_SINGLE_TARGET_SPELL 0x00000020 // 5 Only one target can be apply at a time +#define SPELL_ATTR_EX5_UNK6 0x00000040 // 6 +#define SPELL_ATTR_EX5_UNK7 0x00000080 // 7 +#define SPELL_ATTR_EX5_UNK8 0x00000100 // 8 +#define SPELL_ATTR_EX5_UNK9 0x00000200 // 9 +#define SPELL_ATTR_EX5_UNK10 0x00000400 // 10 +#define SPELL_ATTR_EX5_UNK11 0x00000800 // 11 +#define SPELL_ATTR_EX5_UNK12 0x00001000 // 12 +#define SPELL_ATTR_EX5_UNK13 0x00002000 // 13 +#define SPELL_ATTR_EX5_UNK14 0x00004000 // 14 +#define SPELL_ATTR_EX5_UNK15 0x00008000 // 15 +#define SPELL_ATTR_EX5_UNK16 0x00010000 // 16 +#define SPELL_ATTR_EX5_USABLE_WHILE_FEARED 0x00020000 // 17 usable while feared +#define SPELL_ATTR_EX5_USABLE_WHILE_CONFUSED 0x00040000 // 18 usable while confused +#define SPELL_ATTR_EX5_UNK19 0x00080000 // 19 +#define SPELL_ATTR_EX5_UNK20 0x00100000 // 20 +#define SPELL_ATTR_EX5_UNK21 0x00200000 // 21 +#define SPELL_ATTR_EX5_UNK22 0x00400000 // 22 +#define SPELL_ATTR_EX5_UNK23 0x00800000 // 23 +#define SPELL_ATTR_EX5_UNK24 0x01000000 // 24 +#define SPELL_ATTR_EX5_UNK25 0x02000000 // 25 +#define SPELL_ATTR_EX5_UNK26 0x04000000 // 26 +#define SPELL_ATTR_EX5_UNK27 0x08000000 // 27 +#define SPELL_ATTR_EX5_UNK28 0x10000000 // 28 +#define SPELL_ATTR_EX5_UNK29 0x20000000 // 29 +#define SPELL_ATTR_EX5_UNK30 0x40000000 // 30 +#define SPELL_ATTR_EX5_UNK31 0x80000000 // 31 Forces all nearby enemies to focus attacks caster + +#define SPELL_ATTR_EX6_UNK0 0x00000001 // 0 Only Move spell have this flag +#define SPELL_ATTR_EX6_UNK1 0x00000002 // 1 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK2 0x00000004 // 2 +#define SPELL_ATTR_EX6_UNK3 0x00000008 // 3 +#define SPELL_ATTR_EX6_UNK4 0x00000010 // 4 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK5 0x00000020 // 5 +#define SPELL_ATTR_EX6_UNK6 0x00000040 // 6 +#define SPELL_ATTR_EX6_UNK7 0x00000080 // 7 +#define SPELL_ATTR_EX6_UNK8 0x00000100 // 8 +#define SPELL_ATTR_EX6_UNK9 0x00000200 // 9 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK10 0x00000400 // 10 +#define SPELL_ATTR_EX6_UNK11 0x00000800 // 11 +#define SPELL_ATTR_EX6_UNK12 0x00001000 // 12 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK13 0x00002000 // 13 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK14 0x00004000 // 14 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK15 0x00008000 // 15 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK16 0x00010000 // 16 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK17 0x00020000 // 17 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK18 0x00040000 // 18 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK19 0x00080000 // 19 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK20 0x00100000 // 20 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK21 0x00200000 // 21 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK22 0x00400000 // 22 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK23 0x00800000 // 23 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK24 0x01000000 // 24 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK25 0x02000000 // 25 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK26 0x04000000 // 26 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK27 0x08000000 // 27 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK28 0x10000000 // 28 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK29 0x20000000 // 29 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK30 0x40000000 // 30 not set in 2.4.2 +#define SPELL_ATTR_EX6_UNK31 0x80000000 // 31 not set in 2.4.2 + +enum SheathTypes +{ + SHEATHETYPE_NONE = 0, + SHEATHETYPE_MAINHAND = 1, + SHEATHETYPE_OFFHAND = 2, + SHEATHETYPE_LARGEWEAPONLEFT = 3, + SHEATHETYPE_LARGEWEAPONRIGHT = 4, + SHEATHETYPE_HIPWEAPONLEFT = 5, + SHEATHETYPE_HIPWEAPONRIGHT = 6, + SHEATHETYPE_SHIELD = 7 +}; + +#define MAX_SHEATHETYPE 8 + +enum CharacterSlot +{ + SLOT_HEAD = 0, + SLOT_NECK = 1, + SLOT_SHOULDERS = 2, + SLOT_SHIRT = 3, + SLOT_CHEST = 4, + SLOT_WAIST = 5, + SLOT_LEGS = 6, + SLOT_FEET = 7, + SLOT_WRISTS = 8, + SLOT_HANDS = 9, + SLOT_FINGER1 = 10, + SLOT_FINGER2 = 11, + SLOT_TRINKET1 = 12, + SLOT_TRINKET2 = 13, + SLOT_BACK = 14, + SLOT_MAIN_HAND = 15, + SLOT_OFF_HAND = 16, + SLOT_RANGED = 17, + SLOT_TABARD = 18, + SLOT_EMPTY = 19 +}; + +enum Language +{ + LANG_UNIVERSAL = 0, + LANG_ORCISH = 1, + LANG_DARNASSIAN = 2, + LANG_TAURAHE = 3, + LANG_DWARVISH = 6, + LANG_COMMON = 7, + LANG_DEMONIC = 8, + LANG_TITAN = 9, + LANG_THALASSIAN = 10, + LANG_DRACONIC = 11, + LANG_KALIMAG = 12, + LANG_GNOMISH = 13, + LANG_TROLL = 14, + LANG_GUTTERSPEAK = 33, + LANG_DRAENEI = 35, + LANG_ZOMBIE = 36, + LANG_GNOMISH_BINARY = 37, + LANG_GOBLIN_BINARY = 38, + LANG_ADDON = 0xFFFFFFFF // used by addons, in 2.4.0 not exit, replaced by messagetype? +}; + +#define LANGUAGES_COUNT 19 + +enum Team +{ + HORDE = 67, + ALLIANCE = 469, + //TEAM_STEAMWHEEDLE_CARTEL = 169, // not used in code + //TEAM_ALLIANCE_FORCES = 891, + //TEAM_HORDE_FORCES = 892, + //TEAM_SANCTUARY = 936, + //TEAM_OUTLAND = 980, + //TEAM_OTHER = 0, // if ReputationListId > 0 && Flags != FACTION_FLAG_TEAM_HEADER +}; + +enum SpellEffects +{ + SPELL_EFFECT_INSTAKILL = 1, + SPELL_EFFECT_SCHOOL_DAMAGE = 2, + SPELL_EFFECT_DUMMY = 3, + SPELL_EFFECT_PORTAL_TELEPORT = 4, + SPELL_EFFECT_TELEPORT_UNITS = 5, + SPELL_EFFECT_APPLY_AURA = 6, + SPELL_EFFECT_ENVIRONMENTAL_DAMAGE = 7, + SPELL_EFFECT_POWER_DRAIN = 8, + SPELL_EFFECT_HEALTH_LEECH = 9, + SPELL_EFFECT_HEAL = 10, + SPELL_EFFECT_BIND = 11, + SPELL_EFFECT_PORTAL = 12, + SPELL_EFFECT_RITUAL_BASE = 13, + SPELL_EFFECT_RITUAL_SPECIALIZE = 14, + SPELL_EFFECT_RITUAL_ACTIVATE_PORTAL = 15, + SPELL_EFFECT_QUEST_COMPLETE = 16, + SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL = 17, + SPELL_EFFECT_RESURRECT = 18, + SPELL_EFFECT_ADD_EXTRA_ATTACKS = 19, + SPELL_EFFECT_DODGE = 20, + SPELL_EFFECT_EVADE = 21, + SPELL_EFFECT_PARRY = 22, + SPELL_EFFECT_BLOCK = 23, + SPELL_EFFECT_CREATE_ITEM = 24, + SPELL_EFFECT_WEAPON = 25, + SPELL_EFFECT_DEFENSE = 26, + SPELL_EFFECT_PERSISTENT_AREA_AURA = 27, + SPELL_EFFECT_SUMMON = 28, + SPELL_EFFECT_LEAP = 29, + SPELL_EFFECT_ENERGIZE = 30, + SPELL_EFFECT_WEAPON_PERCENT_DAMAGE = 31, + SPELL_EFFECT_TRIGGER_MISSILE = 32, + SPELL_EFFECT_OPEN_LOCK = 33, + SPELL_EFFECT_SUMMON_CHANGE_ITEM = 34, + SPELL_EFFECT_APPLY_AREA_AURA_PARTY = 35, + SPELL_EFFECT_LEARN_SPELL = 36, + SPELL_EFFECT_SPELL_DEFENSE = 37, + SPELL_EFFECT_DISPEL = 38, + SPELL_EFFECT_LANGUAGE = 39, + SPELL_EFFECT_DUAL_WIELD = 40, + SPELL_EFFECT_SUMMON_WILD = 41, + SPELL_EFFECT_SUMMON_GUARDIAN = 42, + SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER= 43, + SPELL_EFFECT_SKILL_STEP = 44, + SPELL_EFFECT_UNDEFINED_45 = 45, + SPELL_EFFECT_SPAWN = 46, + SPELL_EFFECT_TRADE_SKILL = 47, + SPELL_EFFECT_STEALTH = 48, + SPELL_EFFECT_DETECT = 49, + // SPELL_EFFECT_SUMMON_OBJECT = 50, + SPELL_EFFECT_TRANS_DOOR = 50, + SPELL_EFFECT_FORCE_CRITICAL_HIT = 51, + SPELL_EFFECT_GUARANTEE_HIT = 52, + SPELL_EFFECT_ENCHANT_ITEM = 53, + SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY = 54, + SPELL_EFFECT_TAMECREATURE = 55, + SPELL_EFFECT_SUMMON_PET = 56, + SPELL_EFFECT_LEARN_PET_SPELL = 57, + SPELL_EFFECT_WEAPON_DAMAGE = 58, + SPELL_EFFECT_OPEN_LOCK_ITEM = 59, + SPELL_EFFECT_PROFICIENCY = 60, + SPELL_EFFECT_SEND_EVENT = 61, + SPELL_EFFECT_POWER_BURN = 62, + SPELL_EFFECT_THREAT = 63, + SPELL_EFFECT_TRIGGER_SPELL = 64, + SPELL_EFFECT_HEALTH_FUNNEL = 65, + SPELL_EFFECT_POWER_FUNNEL = 66, + SPELL_EFFECT_HEAL_MAX_HEALTH = 67, + SPELL_EFFECT_INTERRUPT_CAST = 68, + SPELL_EFFECT_DISTRACT = 69, + SPELL_EFFECT_PULL = 70, + SPELL_EFFECT_PICKPOCKET = 71, + SPELL_EFFECT_ADD_FARSIGHT = 72, + SPELL_EFFECT_SUMMON_POSSESSED = 73, + SPELL_EFFECT_SUMMON_TOTEM = 74, + SPELL_EFFECT_HEAL_MECHANICAL = 75, + SPELL_EFFECT_SUMMON_OBJECT_WILD = 76, + SPELL_EFFECT_SCRIPT_EFFECT = 77, + SPELL_EFFECT_ATTACK = 78, + SPELL_EFFECT_SANCTUARY = 79, + SPELL_EFFECT_ADD_COMBO_POINTS = 80, + SPELL_EFFECT_CREATE_HOUSE = 81, + SPELL_EFFECT_BIND_SIGHT = 82, + SPELL_EFFECT_DUEL = 83, + SPELL_EFFECT_STUCK = 84, + SPELL_EFFECT_SUMMON_PLAYER = 85, + SPELL_EFFECT_ACTIVATE_OBJECT = 86, + SPELL_EFFECT_SUMMON_TOTEM_SLOT1 = 87, + SPELL_EFFECT_SUMMON_TOTEM_SLOT2 = 88, + SPELL_EFFECT_SUMMON_TOTEM_SLOT3 = 89, + SPELL_EFFECT_SUMMON_TOTEM_SLOT4 = 90, + SPELL_EFFECT_THREAT_ALL = 91, + SPELL_EFFECT_ENCHANT_HELD_ITEM = 92, + SPELL_EFFECT_SUMMON_PHANTASM = 93, + SPELL_EFFECT_SELF_RESURRECT = 94, + SPELL_EFFECT_SKINNING = 95, + SPELL_EFFECT_CHARGE = 96, + SPELL_EFFECT_SUMMON_CRITTER = 97, + SPELL_EFFECT_KNOCK_BACK = 98, + SPELL_EFFECT_DISENCHANT = 99, + SPELL_EFFECT_INEBRIATE = 100, + SPELL_EFFECT_FEED_PET = 101, + SPELL_EFFECT_DISMISS_PET = 102, + SPELL_EFFECT_REPUTATION = 103, + SPELL_EFFECT_SUMMON_OBJECT_SLOT1 = 104, + SPELL_EFFECT_SUMMON_OBJECT_SLOT2 = 105, + SPELL_EFFECT_SUMMON_OBJECT_SLOT3 = 106, + SPELL_EFFECT_SUMMON_OBJECT_SLOT4 = 107, + SPELL_EFFECT_DISPEL_MECHANIC = 108, + SPELL_EFFECT_SUMMON_DEAD_PET = 109, + SPELL_EFFECT_DESTROY_ALL_TOTEMS = 110, + SPELL_EFFECT_DURABILITY_DAMAGE = 111, + SPELL_EFFECT_SUMMON_DEMON = 112, + SPELL_EFFECT_RESURRECT_NEW = 113, + SPELL_EFFECT_ATTACK_ME = 114, + SPELL_EFFECT_DURABILITY_DAMAGE_PCT = 115, + SPELL_EFFECT_SKIN_PLAYER_CORPSE = 116, + SPELL_EFFECT_SPIRIT_HEAL = 117, + SPELL_EFFECT_SKILL = 118, + SPELL_EFFECT_APPLY_AREA_AURA_PET = 119, + SPELL_EFFECT_TELEPORT_GRAVEYARD = 120, + SPELL_EFFECT_NORMALIZED_WEAPON_DMG = 121, + SPELL_EFFECT_122 = 122, + SPELL_EFFECT_SEND_TAXI = 123, + SPELL_EFFECT_PLAYER_PULL = 124, + SPELL_EFFECT_MODIFY_THREAT_PERCENT = 125, + SPELL_EFFECT_STEAL_BENEFICIAL_BUFF = 126, + SPELL_EFFECT_PROSPECTING = 127, + SPELL_EFFECT_APPLY_AREA_AURA_FRIEND = 128, + SPELL_EFFECT_APPLY_AREA_AURA_ENEMY = 129, + SPELL_EFFECT_REDIRECT_THREAT = 130, + SPELL_EFFECT_131 = 131, + SPELL_EFFECT_132 = 132, + SPELL_EFFECT_UNLEARN_SPECIALIZATION = 133, + SPELL_EFFECT_KILL_CREDIT = 134, + SPELL_EFFECT_135 = 135, + SPELL_EFFECT_HEAL_PCT = 136, + SPELL_EFFECT_ENERGIZE_PCT = 137, + SPELL_EFFECT_138 = 138, + SPELL_EFFECT_139 = 139, + SPELL_EFFECT_FORCE_CAST = 140, + SPELL_EFFECT_141 = 141, + SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE = 142, + SPELL_EFFECT_APPLY_AREA_AURA_OWNER = 143, + SPELL_EFFECT_144 = 144, + SPELL_EFFECT_145 = 145, + SPELL_EFFECT_146 = 146, + SPELL_EFFECT_QUEST_FAIL = 147, + SPELL_EFFECT_148 = 148, + SPELL_EFFECT_149 = 149, + SPELL_EFFECT_150 = 150, + SPELL_EFFECT_TRIGGER_SPELL_2 = 151, + SPELL_EFFECT_152 = 152, + SPELL_EFFECT_153 = 153, + TOTAL_SPELL_EFFECTS = 154 +}; + +// Spell aura states +enum AuraState +{ // (C) used in caster aura state (T) used in target aura state + // (c) used in caster aura state-not (t) used in target aura state-not + AURA_STATE_DEFENSE = 1, // C | + AURA_STATE_HEALTHLESS_20_PERCENT = 2, // CcT | + AURA_STATE_BERSERKING = 3, // C T | + //AURA_STATE_UNKNOWN4 = 4, // c t| some limitation to charge spells (?) and target test spells + AURA_STATE_JUDGEMENT = 5, // C | + //AURA_STATE_UNKNOWN6 = 6, // | not used + AURA_STATE_HUNTER_PARRY = 7, // C | + AURA_STATE_ROGUE_ATTACK_FROM_STEALTH = 7, // C | FIX ME: not implemented yet! + //AURA_STATE_UNKNOWN7c = 7, // c | random/focused bursts spells (?) + //AURA_STATE_UNKNOWN8 = 8, // | not used + //AURA_STATE_UNKNOWN9 = 9, // | not used + AURA_STATE_WARRIOR_VICTORY_RUSH = 10, // C | warrior victory rush + AURA_STATE_HUNTER_CRIT_STRIKE = 10, // C | hunter crit strike + AURA_STATE_CRIT = 11, // C | + AURA_STATE_FAERIE_FIRE = 12, // c t| + AURA_STATE_HEALTHLESS_35_PERCENT = 13, // C T | + AURA_STATE_IMMOLATE = 14, // T | + AURA_STATE_SWIFTMEND = 15, // T | + AURA_STATE_DEADLY_POISON = 16, // T | + AURA_STATE_FORBEARANCE = 17, // c t| + AURA_STATE_WEAKENED_SOUL = 18, // t| + AURA_STATE_HYPOTHERMIA = 19 // c | +}; + +// Spell mechanics +enum Mechanics +{ + MECHANIC_NONE = 0, + MECHANIC_CHARM = 1, + MECHANIC_CONFUSED = 2, + MECHANIC_DISARM = 3, + MECHANIC_DISTRACT = 4, + MECHANIC_FEAR = 5, + MECHANIC_FUMBLE = 6, + MECHANIC_ROOT = 7, + MECHANIC_PACIFY = 8, //0 spells use this mechanic + MECHANIC_SILENCE = 9, + MECHANIC_SLEEP = 10, + MECHANIC_SNARE = 11, + MECHANIC_STUN = 12, + MECHANIC_FREEZE = 13, + MECHANIC_KNOCKOUT = 14, + MECHANIC_BLEED = 15, + MECHANIC_BANDAGE = 16, + MECHANIC_POLYMORPH = 17, + MECHANIC_BANISH = 18, + MECHANIC_SHIELD = 19, + MECHANIC_SHACKLE = 20, + MECHANIC_MOUNT = 21, + MECHANIC_PERSUADE = 22, //0 spells use this mechanic + MECHANIC_TURN = 23, + MECHANIC_HORROR = 24, + MECHANIC_INVULNERABILITY = 25, + MECHANIC_INTERRUPT = 26, + MECHANIC_DAZE = 27, + MECHANIC_DISCOVERY = 28, + MECHANIC_IMMUNE_SHIELD = 29, // Divine (Blessing) Shield/Protection and Ice Block + MECHANIC_SAPPED = 30 +}; + +// Used for spell 42292 Immune Movement Impairment and Loss of Control (0x49967da6) +#define IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK ( \ + (1<(only this effect in the spell) can't cast to it, +//some aura(related to Mechanics or ImmuneToState) can't apply to it. +enum SpellImmunity +{ + IMMUNITY_EFFECT = 0, // enum SpellEffects + IMMUNITY_STATE = 1, // enum AuraType + IMMUNITY_SCHOOL = 2, // enum SpellSchoolMask + IMMUNITY_DAMAGE = 3, // enum SpellSchoolMask + IMMUNITY_DISPEL = 4, // enum DispelType + IMMUNITY_MECHANIC = 5 // enum Mechanics +}; + +#define MAX_SPELL_IMMUNITY 6 + +enum Targets +{ + TARGET_SELF = 1, + TARGET_RANDOM_ENEMY_CHAIN_IN_AREA = 2, // only one spell has that, but regardless, it's a target type after all + TARGET_PET = 5, + TARGET_CHAIN_DAMAGE = 6, + TARGET_AREAEFFECT_CUSTOM = 8, + TARGET_INNKEEPER_COORDINATES = 9, // uses in teleport to innkeeper spells + TARGET_ALL_ENEMY_IN_AREA = 15, + TARGET_ALL_ENEMY_IN_AREA_INSTANT = 16, + TARGET_TABLE_X_Y_Z_COORDINATES = 17, // uses in teleport spells and some other + TARGET_EFFECT_SELECT = 18, // highly depends on the spell effect + TARGET_ALL_PARTY_AROUND_CASTER = 20, + TARGET_SINGLE_FRIEND = 21, + TARGET_ALL_AROUND_CASTER = 22, // used only in TargetA, target selection dependent from TargetB + TARGET_GAMEOBJECT = 23, + TARGET_IN_FRONT_OF_CASTER = 24, + TARGET_DUELVSPLAYER = 25, + TARGET_GAMEOBJECT_ITEM = 26, + TARGET_MASTER = 27, + TARGET_ALL_ENEMY_IN_AREA_CHANNELED = 28, + TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER = 30, // in TargetB used only with TARGET_ALL_AROUND_CASTER and in self casting range in TargetA + TARGET_ALL_FRIENDLY_UNITS_IN_AREA = 31, + TARGET_MINION = 32, + TARGET_ALL_PARTY = 33, + TARGET_ALL_PARTY_AROUND_CASTER_2 = 34, // used in Tranquility + TARGET_SINGLE_PARTY = 35, + TARGET_AREAEFFECT_PARTY = 37, + TARGET_SCRIPT = 38, + TARGET_SELF_FISHING = 39, + TARGET_TOTEM_EARTH = 41, + TARGET_TOTEM_WATER = 42, + TARGET_TOTEM_AIR = 43, + TARGET_TOTEM_FIRE = 44, + TARGET_CHAIN_HEAL = 45, + TARGET_SCRIPT_COORDINATES = 46, + TARGET_DYNAMIC_OBJECT = 47, + TARGET_SUMMON = 48, + TARGET_AREAEFFECT_CUSTOM_2 = 52, + TARGET_CURRENT_ENEMY_COORDINATES = 53, // set unit coordinates as dest, only 16 target B imlemented + TARGET_RANDOM_RAID_MEMBER = 56, + TARGET_SINGLE_FRIEND_2 = 57, + TARGET_AREAEFFECT_PARTY_AND_CLASS = 61, + TARGET_DUELVSPLAYER_COORDINATES = 63, + TARGET_BEHIND_VICTIM = 65, // uses in teleport behind spells + TARGET_SINGLE_ENEMY = 77, + TARGET_SELF2 = 87, + TARGET_NONCOMBAT_PET = 90, +}; + +enum SpellMissInfo +{ + SPELL_MISS_NONE = 0, + SPELL_MISS_MISS = 1, + SPELL_MISS_RESIST = 2, + SPELL_MISS_DODGE = 3, + SPELL_MISS_PARRY = 4, + SPELL_MISS_BLOCK = 5, + SPELL_MISS_EVADE = 6, + SPELL_MISS_IMMUNE = 7, + SPELL_MISS_IMMUNE2 = 8, + SPELL_MISS_DEFLECT = 9, + SPELL_MISS_ABSORB = 10, + SPELL_MISS_REFLECT = 11, +}; + +enum SpellHitType +{ + SPELL_HIT_TYPE_UNK1 = 0x00001, + SPELL_HIT_TYPE_CRIT = 0x00002, + SPELL_HIT_TYPE_UNK2 = 0x00004, + SPELL_HIT_TYPE_UNK3 = 0x00008, + SPELL_HIT_TYPE_UNK4 = 0x00020 +}; + +enum SpellDmgClass +{ + SPELL_DAMAGE_CLASS_NONE = 0, + SPELL_DAMAGE_CLASS_MAGIC = 1, + SPELL_DAMAGE_CLASS_MELEE = 2, + SPELL_DAMAGE_CLASS_RANGED = 3 +}; + +enum SpellPreventionType +{ + SPELL_PREVENTION_TYPE_NONE = 0, + SPELL_PREVENTION_TYPE_SILENCE = 1, + SPELL_PREVENTION_TYPE_PACIFY = 2 +}; + +enum GameobjectTypes +{ + GAMEOBJECT_TYPE_DOOR = 0, + GAMEOBJECT_TYPE_BUTTON = 1, + GAMEOBJECT_TYPE_QUESTGIVER = 2, + GAMEOBJECT_TYPE_CHEST = 3, + GAMEOBJECT_TYPE_BINDER = 4, + GAMEOBJECT_TYPE_GENERIC = 5, + GAMEOBJECT_TYPE_TRAP = 6, + GAMEOBJECT_TYPE_CHAIR = 7, + GAMEOBJECT_TYPE_SPELL_FOCUS = 8, + GAMEOBJECT_TYPE_TEXT = 9, + GAMEOBJECT_TYPE_GOOBER = 10, + GAMEOBJECT_TYPE_TRANSPORT = 11, + GAMEOBJECT_TYPE_AREADAMAGE = 12, + GAMEOBJECT_TYPE_CAMERA = 13, + GAMEOBJECT_TYPE_MAP_OBJECT = 14, + GAMEOBJECT_TYPE_MO_TRANSPORT = 15, + GAMEOBJECT_TYPE_DUEL_ARBITER = 16, + GAMEOBJECT_TYPE_FISHINGNODE = 17, + GAMEOBJECT_TYPE_SUMMONING_RITUAL = 18, + GAMEOBJECT_TYPE_MAILBOX = 19, + GAMEOBJECT_TYPE_AUCTIONHOUSE = 20, + GAMEOBJECT_TYPE_GUARDPOST = 21, + GAMEOBJECT_TYPE_SPELLCASTER = 22, + GAMEOBJECT_TYPE_MEETINGSTONE = 23, + GAMEOBJECT_TYPE_FLAGSTAND = 24, + GAMEOBJECT_TYPE_FISHINGHOLE = 25, + GAMEOBJECT_TYPE_FLAGDROP = 26, + GAMEOBJECT_TYPE_MINI_GAME = 27, + GAMEOBJECT_TYPE_LOTTERY_KIOSK = 28, + GAMEOBJECT_TYPE_CAPTURE_POINT = 29, + GAMEOBJECT_TYPE_AURA_GENERATOR = 30, + GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY = 31, + GAMEOBJECT_TYPE_DO_NOT_USE_YET = 32, + GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING = 33, + GAMEOBJECT_TYPE_GUILD_BANK = 34, +}; + +#define MAX_GAMEOBJECT_TYPE 35 // sending to client this or greater value can crash client. + +#define GAMEOBJECT_FISHINGNODE_ENTRY 35591 // Better to define it somewhere instead of hardcoding everywhere + +enum GameObjectFlags +{ + GO_FLAG_IN_USE = 0x01, //disables interaction while animated + GO_FLAG_LOCKED = 0x02, //require key, spell, event, etc to be opened. Makes "Locked" appear in tooltip + GO_FLAG_INTERACT_COND = 0x04, //cannot interact (condition to interact) + GO_FLAG_TRANSPORT = 0x08, //any kind of transport? Object can transport (elevator, boat, car) + GO_FLAG_UNK1 = 0x10, // + GO_FLAG_NODESPAWN = 0x20, //never despawn, typically for doors, they just change state + GO_FLAG_TRIGGERED = 0x40, //typically, summoned objects. Triggered by spell or other events +}; + +enum TextEmotes +{ + TEXTEMOTE_AGREE = 1, + TEXTEMOTE_AMAZE = 2, + TEXTEMOTE_ANGRY = 3, + TEXTEMOTE_APOLOGIZE = 4, + TEXTEMOTE_APPLAUD = 5, + TEXTEMOTE_BASHFUL = 6, + TEXTEMOTE_BECKON = 7, + TEXTEMOTE_BEG = 8, + TEXTEMOTE_BITE = 9, + TEXTEMOTE_BLEED = 10, + TEXTEMOTE_BLINK = 11, + TEXTEMOTE_BLUSH = 12, + TEXTEMOTE_BONK = 13, + TEXTEMOTE_BORED = 14, + TEXTEMOTE_BOUNCE = 15, + TEXTEMOTE_BRB = 16, + TEXTEMOTE_BOW = 17, + TEXTEMOTE_BURP = 18, + TEXTEMOTE_BYE = 19, + TEXTEMOTE_CACKLE = 20, + TEXTEMOTE_CHEER = 21, + TEXTEMOTE_CHICKEN = 22, + TEXTEMOTE_CHUCKLE = 23, + TEXTEMOTE_CLAP = 24, + TEXTEMOTE_CONFUSED = 25, + TEXTEMOTE_CONGRATULATE = 26, + TEXTEMOTE_COUGH = 27, + TEXTEMOTE_COWER = 28, + TEXTEMOTE_CRACK = 29, + TEXTEMOTE_CRINGE = 30, + TEXTEMOTE_CRY = 31, + TEXTEMOTE_CURIOUS = 32, + TEXTEMOTE_CURTSEY = 33, + TEXTEMOTE_DANCE = 34, + TEXTEMOTE_DRINK = 35, + TEXTEMOTE_DROOL = 36, + TEXTEMOTE_EAT = 37, + TEXTEMOTE_EYE = 38, + TEXTEMOTE_FART = 39, + TEXTEMOTE_FIDGET = 40, + TEXTEMOTE_FLEX = 41, + TEXTEMOTE_FROWN = 42, + TEXTEMOTE_GASP = 43, + TEXTEMOTE_GAZE = 44, + TEXTEMOTE_GIGGLE = 45, + TEXTEMOTE_GLARE = 46, + TEXTEMOTE_GLOAT = 47, + TEXTEMOTE_GREET = 48, + TEXTEMOTE_GRIN = 49, + TEXTEMOTE_GROAN = 50, + TEXTEMOTE_GROVEL = 51, + TEXTEMOTE_GUFFAW = 52, + TEXTEMOTE_HAIL = 53, + TEXTEMOTE_HAPPY = 54, + TEXTEMOTE_HELLO = 55, + TEXTEMOTE_HUG = 56, + TEXTEMOTE_HUNGRY = 57, + TEXTEMOTE_KISS = 58, + TEXTEMOTE_KNEEL = 59, + TEXTEMOTE_LAUGH = 60, + TEXTEMOTE_LAYDOWN = 61, + TEXTEMOTE_MESSAGE = 62, + TEXTEMOTE_MOAN = 63, + TEXTEMOTE_MOON = 64, + TEXTEMOTE_MOURN = 65, + TEXTEMOTE_NO = 66, + TEXTEMOTE_NOD = 67, + TEXTEMOTE_NOSEPICK = 68, + TEXTEMOTE_PANIC = 69, + TEXTEMOTE_PEER = 70, + TEXTEMOTE_PLEAD = 71, + TEXTEMOTE_POINT = 72, + TEXTEMOTE_POKE = 73, + TEXTEMOTE_PRAY = 74, + TEXTEMOTE_ROAR = 75, + TEXTEMOTE_ROFL = 76, + TEXTEMOTE_RUDE = 77, + TEXTEMOTE_SALUTE = 78, + TEXTEMOTE_SCRATCH = 79, + TEXTEMOTE_SEXY = 80, + TEXTEMOTE_SHAKE = 81, + TEXTEMOTE_SHOUT = 82, + TEXTEMOTE_SHRUG = 83, + TEXTEMOTE_SHY = 84, + TEXTEMOTE_SIGH = 85, + TEXTEMOTE_SIT = 86, + TEXTEMOTE_SLEEP = 87, + TEXTEMOTE_SNARL = 88, + TEXTEMOTE_SPIT = 89, + TEXTEMOTE_STARE = 90, + TEXTEMOTE_SURPRISED = 91, + TEXTEMOTE_SURRENDER = 92, + TEXTEMOTE_TALK = 93, + TEXTEMOTE_TALKEX = 94, + TEXTEMOTE_TALKQ = 95, + TEXTEMOTE_TAP = 96, + TEXTEMOTE_THANK = 97, + TEXTEMOTE_THREATEN = 98, + TEXTEMOTE_TIRED = 99, + TEXTEMOTE_VICTORY = 100, + TEXTEMOTE_WAVE = 101, + TEXTEMOTE_WELCOME = 102, + TEXTEMOTE_WHINE = 103, + TEXTEMOTE_WHISTLE = 104, + TEXTEMOTE_WORK = 105, + TEXTEMOTE_YAWN = 106, + TEXTEMOTE_BOGGLE = 107, + TEXTEMOTE_CALM = 108, + TEXTEMOTE_COLD = 109, + TEXTEMOTE_COMFORT = 110, + TEXTEMOTE_CUDDLE = 111, + TEXTEMOTE_DUCK = 112, + TEXTEMOTE_INSULT = 113, + TEXTEMOTE_INTRODUCE = 114, + TEXTEMOTE_JK = 115, + TEXTEMOTE_LICK = 116, + TEXTEMOTE_LISTEN = 117, + TEXTEMOTE_LOST = 118, + TEXTEMOTE_MOCK = 119, + TEXTEMOTE_PONDER = 120, + TEXTEMOTE_POUNCE = 121, + TEXTEMOTE_PRAISE = 122, + TEXTEMOTE_PURR = 123, + TEXTEMOTE_PUZZLE = 124, + TEXTEMOTE_RAISE = 125, + TEXTEMOTE_READY = 126, + TEXTEMOTE_SHIMMY = 127, + TEXTEMOTE_SHIVER = 128, + TEXTEMOTE_SHOO = 129, + TEXTEMOTE_SLAP = 130, + TEXTEMOTE_SMIRK = 131, + TEXTEMOTE_SNIFF = 132, + TEXTEMOTE_SNUB = 133, + TEXTEMOTE_SOOTHE = 134, + TEXTEMOTE_STINK = 135, + TEXTEMOTE_TAUNT = 136, + TEXTEMOTE_TEASE = 137, + TEXTEMOTE_THIRSTY = 138, + TEXTEMOTE_VETO = 139, + TEXTEMOTE_SNICKER = 140, + TEXTEMOTE_STAND = 141, + TEXTEMOTE_TICKLE = 142, + TEXTEMOTE_VIOLIN = 143, + TEXTEMOTE_SMILE = 163, + TEXTEMOTE_RASP = 183, + TEXTEMOTE_PITY = 203, + TEXTEMOTE_GROWL = 204, + TEXTEMOTE_BARK = 205, + TEXTEMOTE_SCARED = 223, + TEXTEMOTE_FLOP = 224, + TEXTEMOTE_LOVE = 225, + TEXTEMOTE_MOO = 226, + TEXTEMOTE_OPENFIRE = 327, + TEXTEMOTE_FLIRT = 328, + TEXTEMOTE_JOKE = 329, + TEXTEMOTE_COMMEND = 243, + TEXTEMOTE_WINK = 363, + TEXTEMOTE_PAT = 364, + TEXTEMOTE_SERIOUS = 365, + TEXTEMOTE_MOUNTSPECIAL = 366, + TEXTEMOTE_GOODLUCK = 367, + TEXTEMOTE_BLAME = 368, + TEXTEMOTE_BLANK = 369, + TEXTEMOTE_BRANDISH = 370, + TEXTEMOTE_BREATH = 371, + TEXTEMOTE_DISAGREE = 372, + TEXTEMOTE_DOUBT = 373, + TEXTEMOTE_EMBARRASS = 374, + TEXTEMOTE_ENCOURAGE = 375, + TEXTEMOTE_ENEMY = 376, + TEXTEMOTE_EYEBROW = 377, + TEXTEMOTE_TOAST = 378 +}; + +enum Emote +{ + EMOTE_ONESHOT_NONE = 0, + EMOTE_ONESHOT_TALK = 1, + EMOTE_ONESHOT_BOW = 2, + EMOTE_ONESHOT_WAVE = 3, + EMOTE_ONESHOT_CHEER = 4, + EMOTE_ONESHOT_EXCLAMATION = 5, + EMOTE_ONESHOT_QUESTION = 6, + EMOTE_ONESHOT_EAT = 7, + EMOTE_STATE_DANCE = 10, + EMOTE_ONESHOT_LAUGH = 11, + EMOTE_STATE_SLEEP = 12, + EMOTE_STATE_SIT = 13, + EMOTE_ONESHOT_RUDE = 14, + EMOTE_ONESHOT_ROAR = 15, + EMOTE_ONESHOT_KNEEL = 16, + EMOTE_ONESHOT_KISS = 17, + EMOTE_ONESHOT_CRY = 18, + EMOTE_ONESHOT_CHICKEN = 19, + EMOTE_ONESHOT_BEG = 20, + EMOTE_ONESHOT_APPLAUD = 21, + EMOTE_ONESHOT_SHOUT = 22, + EMOTE_ONESHOT_FLEX = 23, + EMOTE_ONESHOT_SHY = 24, + EMOTE_ONESHOT_POINT = 25, + EMOTE_STATE_STAND = 26, + EMOTE_STATE_READYUNARMED = 27, + EMOTE_STATE_WORK = 28, + EMOTE_STATE_POINT = 29, + EMOTE_STATE_NONE = 30, + EMOTE_ONESHOT_WOUND = 33, + EMOTE_ONESHOT_WOUNDCRITICAL = 34, + EMOTE_ONESHOT_ATTACKUNARMED = 35, + EMOTE_ONESHOT_ATTACK1H = 36, + EMOTE_ONESHOT_ATTACK2HTIGHT = 37, + EMOTE_ONESHOT_ATTACK2HLOOSE = 38, + EMOTE_ONESHOT_PARRYUNARMED = 39, + EMOTE_ONESHOT_PARRYSHIELD = 43, + EMOTE_ONESHOT_READYUNARMED = 44, + EMOTE_ONESHOT_READY1H = 45, + EMOTE_ONESHOT_READYBOW = 48, + EMOTE_ONESHOT_SPELLPRECAST = 50, + EMOTE_ONESHOT_SPELLCAST = 51, + EMOTE_ONESHOT_BATTLEROAR = 53, + EMOTE_ONESHOT_SPECIALATTACK1H = 54, + EMOTE_ONESHOT_KICK = 60, + EMOTE_ONESHOT_ATTACKTHROWN = 61, + EMOTE_STATE_STUN = 64, + EMOTE_STATE_DEAD = 65, + EMOTE_ONESHOT_SALUTE = 66, + EMOTE_STATE_KNEEL = 68, + EMOTE_STATE_USESTANDING = 69, + EMOTE_ONESHOT_WAVE_NOSHEATHE = 70, + EMOTE_ONESHOT_CHEER_NOSHEATHE = 71, + EMOTE_ONESHOT_EAT_NOSHEATHE = 92, + EMOTE_STATE_STUN_NOSHEATHE = 93, + EMOTE_ONESHOT_DANCE = 94, + EMOTE_ONESHOT_SALUTE_NOSHEATH = 113, + EMOTE_STATE_USESTANDING_NOSHEATHE = 133, + EMOTE_ONESHOT_LAUGH_NOSHEATHE = 153, + EMOTE_STATE_WORK_NOSHEATHE = 173, + EMOTE_STATE_SPELLPRECAST = 193, + EMOTE_ONESHOT_READYRIFLE = 213, + EMOTE_STATE_READYRIFLE = 214, + EMOTE_STATE_WORK_NOSHEATHE_MINING = 233, + EMOTE_STATE_WORK_NOSHEATHE_CHOPWOOD= 234, + EMOTE_zzOLDONESHOT_LIFTOFF = 253, + EMOTE_ONESHOT_LIFTOFF = 254, + EMOTE_ONESHOT_YES = 273, + EMOTE_ONESHOT_NO = 274, + EMOTE_ONESHOT_TRAIN = 275, + EMOTE_ONESHOT_LAND = 293, + EMOTE_STATE_AT_EASE = 313, + EMOTE_STATE_READY1H = 333, + EMOTE_STATE_SPELLKNEELSTART = 353, + EMOTE_STATE_SUBMERGED = 373, + EMOTE_ONESHOT_SUBMERGE = 374, + EMOTE_STATE_READY2H = 375, + EMOTE_STATE_READYBOW = 376, + EMOTE_ONESHOT_MOUNTSPECIAL = 377, + EMOTE_STATE_TALK = 378, + EMOTE_STATE_FISHING = 379, + EMOTE_ONESHOT_FISHING = 380, + EMOTE_ONESHOT_LOOT = 381, + EMOTE_STATE_WHIRLWIND = 382, + EMOTE_STATE_DROWNED = 383, + EMOTE_STATE_HOLD_BOW = 384, + EMOTE_STATE_HOLD_RIFLE = 385, + EMOTE_STATE_HOLD_THROWN = 386, + EMOTE_ONESHOT_DROWN = 387, + EMOTE_ONESHOT_STOMP = 388, + EMOTE_ONESHOT_ATTACKOFF = 389, + EMOTE_ONESHOT_ATTACKOFFPIERCE = 390, + EMOTE_STATE_ROAR = 391, + EMOTE_STATE_LAUGH = 392, + EMOTE_ONESHOT_CREATURE_SPECIAL = 393, + EMOTE_ONESHOT_JUMPLANDRUN = 394, + EMOTE_ONESHOT_JUMPEND = 395, + EMOTE_ONESHOT_TALK_NOSHEATHE = 396, + EMOTE_ONESHOT_POINT_NOSHEATHE = 397, + EMOTE_STATE_CANNIBALIZE = 398, + EMOTE_ONESHOT_JUMPSTART = 399, + EMOTE_STATE_DANCESPECIAL = 400, + EMOTE_ONESHOT_DANCESPECIAL = 401, + EMOTE_ONESHOT_CUSTOMSPELL01 = 402, + EMOTE_ONESHOT_CUSTOMSPELL02 = 403, + EMOTE_ONESHOT_CUSTOMSPELL03 = 404, + EMOTE_ONESHOT_CUSTOMSPELL04 = 405, + EMOTE_ONESHOT_CUSTOMSPELL05 = 406, + EMOTE_ONESHOT_CUSTOMSPELL06 = 407, + EMOTE_ONESHOT_CUSTOMSPELL07 = 408, + EMOTE_ONESHOT_CUSTOMSPELL08 = 409, + EMOTE_ONESHOT_CUSTOMSPELL09 = 410, + EMOTE_ONESHOT_CUSTOMSPELL10 = 411, + EMOTE_STATE_EXCLAIM = 412, + EMOTE_STATE_SIT_CHAIR_MED = 415, + EMOTE_STATE_SPELLEFFECT_HOLD = 422 +}; + +enum Anim +{ + ANIM_STAND = 0x0, + ANIM_DEATH = 0x1, + ANIM_SPELL = 0x2, + ANIM_STOP = 0x3, + ANIM_WALK = 0x4, + ANIM_RUN = 0x5, + ANIM_DEAD = 0x6, + ANIM_RISE = 0x7, + ANIM_STANDWOUND = 0x8, + ANIM_COMBATWOUND = 0x9, + ANIM_COMBATCRITICAL = 0xA, + ANIM_SHUFFLE_LEFT = 0xB, + ANIM_SHUFFLE_RIGHT = 0xC, + ANIM_WALK_BACKWARDS = 0xD, + ANIM_STUN = 0xE, + ANIM_HANDS_CLOSED = 0xF, + ANIM_ATTACKUNARMED = 0x10, + ANIM_ATTACK1H = 0x11, + ANIM_ATTACK2HTIGHT = 0x12, + ANIM_ATTACK2HLOOSE = 0x13, + ANIM_PARRYUNARMED = 0x14, + ANIM_PARRY1H = 0x15, + ANIM_PARRY2HTIGHT = 0x16, + ANIM_PARRY2HLOOSE = 0x17, + ANIM_PARRYSHIELD = 0x18, + ANIM_READYUNARMED = 0x19, + ANIM_READY1H = 0x1A, + ANIM_READY2HTIGHT = 0x1B, + ANIM_READY2HLOOSE = 0x1C, + ANIM_READYBOW = 0x1D, + ANIM_DODGE = 0x1E, + ANIM_SPELLPRECAST = 0x1F, + ANIM_SPELLCAST = 0x20, + ANIM_SPELLCASTAREA = 0x21, + ANIM_NPCWELCOME = 0x22, + ANIM_NPCGOODBYE = 0x23, + ANIM_BLOCK = 0x24, + ANIM_JUMPSTART = 0x25, + ANIM_JUMP = 0x26, + ANIM_JUMPEND = 0x27, + ANIM_FALL = 0x28, + ANIM_SWIMIDLE = 0x29, + ANIM_SWIM = 0x2A, + ANIM_SWIM_LEFT = 0x2B, + ANIM_SWIM_RIGHT = 0x2C, + ANIM_SWIM_BACKWARDS = 0x2D, + ANIM_ATTACKBOW = 0x2E, + ANIM_FIREBOW = 0x2F, + ANIM_READYRIFLE = 0x30, + ANIM_ATTACKRIFLE = 0x31, + ANIM_LOOT = 0x32, + ANIM_SPELL_PRECAST_DIRECTED = 0x33, + ANIM_SPELL_PRECAST_OMNI = 0x34, + ANIM_SPELL_CAST_DIRECTED = 0x35, + ANIM_SPELL_CAST_OMNI = 0x36, + ANIM_SPELL_BATTLEROAR = 0x37, + ANIM_SPELL_READYABILITY = 0x38, + ANIM_SPELL_SPECIAL1H = 0x39, + ANIM_SPELL_SPECIAL2H = 0x3A, + ANIM_SPELL_SHIELDBASH = 0x3B, + ANIM_EMOTE_TALK = 0x3C, + ANIM_EMOTE_EAT = 0x3D, + ANIM_EMOTE_WORK = 0x3E, + ANIM_EMOTE_USE_STANDING = 0x3F, + ANIM_EMOTE_EXCLAMATION = 0x40, + ANIM_EMOTE_QUESTION = 0x41, + ANIM_EMOTE_BOW = 0x42, + ANIM_EMOTE_WAVE = 0x43, + ANIM_EMOTE_CHEER = 0x44, + ANIM_EMOTE_DANCE = 0x45, + ANIM_EMOTE_LAUGH = 0x46, + ANIM_EMOTE_SLEEP = 0x47, + ANIM_EMOTE_SIT_GROUND = 0x48, + ANIM_EMOTE_RUDE = 0x49, + ANIM_EMOTE_ROAR = 0x4A, + ANIM_EMOTE_KNEEL = 0x4B, + ANIM_EMOTE_KISS = 0x4C, + ANIM_EMOTE_CRY = 0x4D, + ANIM_EMOTE_CHICKEN = 0x4E, + ANIM_EMOTE_BEG = 0x4F, + ANIM_EMOTE_APPLAUD = 0x50, + ANIM_EMOTE_SHOUT = 0x51, + ANIM_EMOTE_FLEX = 0x52, + ANIM_EMOTE_SHY = 0x53, + ANIM_EMOTE_POINT = 0x54, + ANIM_ATTACK1HPIERCE = 0x55, + ANIM_ATTACK2HLOOSEPIERCE = 0x56, + ANIM_ATTACKOFF = 0x57, + ANIM_ATTACKOFFPIERCE = 0x58, + ANIM_SHEATHE = 0x59, + ANIM_HIPSHEATHE = 0x5A, + ANIM_MOUNT = 0x5B, + ANIM_RUN_LEANRIGHT = 0x5C, + ANIM_RUN_LEANLEFT = 0x5D, + ANIM_MOUNT_SPECIAL = 0x5E, + ANIM_KICK = 0x5F, + ANIM_SITDOWN = 0x60, + ANIM_SITTING = 0x61, + ANIM_SITUP = 0x62, + ANIM_SLEEPDOWN = 0x63, + ANIM_SLEEPING = 0x64, + ANIM_SLEEPUP = 0x65, + ANIM_SITCHAIRLOW = 0x66, + ANIM_SITCHAIRMEDIUM = 0x67, + ANIM_SITCHAIRHIGH = 0x68, + ANIM_LOADBOW = 0x69, + ANIM_LOADRIFLE = 0x6A, + ANIM_ATTACKTHROWN = 0x6B, + ANIM_READYTHROWN = 0x6C, + ANIM_HOLDBOW = 0x6D, + ANIM_HOLDRIFLE = 0x6E, + ANIM_HOLDTHROWN = 0x6F, + ANIM_LOADTHROWN = 0x70, + ANIM_EMOTE_SALUTE = 0x71, + ANIM_KNEELDOWN = 0x72, + ANIM_KNEELING = 0x73, + ANIM_KNEELUP = 0x74, + ANIM_ATTACKUNARMEDOFF = 0x75, + ANIM_SPECIALUNARMED = 0x76, + ANIM_STEALTHWALK = 0x77, + ANIM_STEALTHSTAND = 0x78, + ANIM_KNOCKDOWN = 0x79, + ANIM_EATING = 0x7A, + ANIM_USESTANDINGLOOP = 0x7B, + ANIM_CHANNELCASTDIRECTED = 0x7C, + ANIM_CHANNELCASTOMNI = 0x7D, + ANIM_WHIRLWIND = 0x7E, + ANIM_BIRTH = 0x7F, + ANIM_USESTANDINGSTART = 0x80, + ANIM_USESTANDINGEND = 0x81, + ANIM_HOWL = 0x82, + ANIM_DROWN = 0x83, + ANIM_DROWNED = 0x84, + ANIM_FISHINGCAST = 0x85, + ANIM_FISHINGLOOP = 0x86, + ANIM_FLY = 0x87, + ANIM_EMOTE_WORK_NO_SHEATHE = 0x88, + ANIM_EMOTE_STUN_NO_SHEATHE = 0x89, + ANIM_EMOTE_USE_STANDING_NO_SHEATHE= 0x8A, + ANIM_SPELL_SLEEP_DOWN = 0x8B, + ANIM_SPELL_KNEEL_START = 0x8C, + ANIM_SPELL_KNEEL_LOOP = 0x8D, + ANIM_SPELL_KNEEL_END = 0x8E, + ANIM_SPRINT = 0x8F, + ANIM_IN_FIGHT = 0x90, + + ANIM_GAMEOBJ_SPAWN = 145, + ANIM_GAMEOBJ_CLOSE = 146, + ANIM_GAMEOBJ_CLOSED = 147, + ANIM_GAMEOBJ_OPEN = 148, + ANIM_GAMEOBJ_OPENED = 149, + ANIM_GAMEOBJ_DESTROY = 150, + ANIM_GAMEOBJ_DESTROYED = 151, + ANIM_GAMEOBJ_REBUILD = 152, + ANIM_GAMEOBJ_CUSTOM0 = 153, + ANIM_GAMEOBJ_CUSTOM1 = 154, + ANIM_GAMEOBJ_CUSTOM2 = 155, + ANIM_GAMEOBJ_CUSTOM3 = 156, + ANIM_GAMEOBJ_DESPAWN = 157, + ANIM_HOLD = 158, + ANIM_DECAY = 159, + ANIM_BOWPULL = 160, + ANIM_BOWRELEASE = 161, + ANIM_SHIPSTART = 162, + ANIM_SHIPMOVEING = 163, + ANIM_SHIPSTOP = 164, + ANIM_GROUPARROW = 165, + ANIM_ARROW = 166, + ANIM_CORPSEARROW = 167, + ANIM_GUIDEARROW = 168, + ANIM_SWAY = 169, + ANIM_DRUIDCATPOUNCE = 170, + ANIM_DRUIDCATRIP = 171, + ANIM_DRUIDCATRAKE = 172, + ANIM_DRUIDCATRAVAGE = 173, + ANIM_DRUIDCATCLAW = 174, + ANIM_DRUIDCATCOWER = 175, + ANIM_DRUIDBEARSWIPE = 176, + ANIM_DRUIDBEARBITE = 177, + ANIM_DRUIDBEARMAUL = 178, + ANIM_DRUIDBEARBASH = 179, + ANIM_DRAGONTAIL = 180, + ANIM_DRAGONSTOMP = 181, + ANIM_DRAGONSPIT = 182, + ANIM_DRAGONSPITHOVER = 183, + ANIM_DRAGONSPITFLY = 184, + ANIM_EMOTEYES = 185, + ANIM_EMOTENO = 186, + ANIM_JUMPLANDRUN = 187, + ANIM_LOOTHOLD = 188, + ANIM_LOOTUP = 189, + ANIM_STANDHIGH = 190, + ANIM_IMPACT = 191, + ANIM_LIFTOFF = 192, + ANIM_HOVER = 193, + ANIM_SUCCUBUSENTICE = 194, + ANIM_EMOTETRAIN = 195, + ANIM_EMOTEDEAD = 196, + ANIM_EMOTEDANCEONCE = 197, + ANIM_DEFLECT = 198, + ANIM_EMOTEEATNOSHEATHE = 199, + ANIM_LAND = 200, + ANIM_SUBMERGE = 201, + ANIM_SUBMERGED = 202, + ANIM_CANNIBALIZE = 203, + ANIM_ARROWBIRTH = 204, + ANIM_GROURARROWBIRTH = 205, + ANIM_CORPSEARROWBIRTH = 206, + ANIM_GUIDEARROWBIRTH = 207, + ANIM_EMOTETALKNOSHEATHE = 208, + ANIM_EMOTEPOINTNOSHEATHE = 209, + ANIM_EMOTESALUTENOSHEATHE = 210, + ANIM_EMOTEDANCESPECIAL = 211, + ANIM_MUTILATE = 212, + ANIM_CUSTOMSPELL01 = 213, + ANIM_CUSTOMSPELL02 = 214, + ANIM_CUSTOMSPELL03 = 215, + ANIM_CUSTOMSPELL04 = 216, + ANIM_CUSTOMSPELL05 = 217, + ANIM_CUSTOMSPELL06 = 218, + ANIM_CUSTOMSPELL07 = 219, + ANIM_CUSTOMSPELL08 = 220, + ANIM_CUSTOMSPELL09 = 221, + ANIM_CUSTOMSPELL10 = 222, + ANIM_StealthRun = 223 +}; + +enum LockKeyType +{ + LOCK_KEY_NONE = 0, + LOCK_KEY_ITEM = 1, + LOCK_KEY_SKILL = 2 +}; + +enum LockType +{ + LOCKTYPE_PICKLOCK = 1, + LOCKTYPE_HERBALISM = 2, + LOCKTYPE_MINING = 3, + LOCKTYPE_DISARM_TRAP = 4, + LOCKTYPE_OPEN = 5, + LOCKTYPE_TREASURE = 6, + LOCKTYPE_CALCIFIED_ELVEN_GEMS = 7, + LOCKTYPE_CLOSE = 8, + LOCKTYPE_ARM_TRAP = 9, + LOCKTYPE_QUICK_OPEN = 10, + LOCKTYPE_QUICK_CLOSE = 11, + LOCKTYPE_OPEN_TINKERING = 12, + LOCKTYPE_OPEN_KNEELING = 13, + LOCKTYPE_OPEN_ATTACKING = 14, + LOCKTYPE_GAHZRIDIAN = 15, + LOCKTYPE_BLASTING = 16, + LOCKTYPE_SLOW_OPEN = 17, + LOCKTYPE_SLOW_CLOSE = 18, + LOCKTYPE_FISHING = 19 +}; + +enum TrainerType // this is important type for npcs! +{ + TRAINER_TYPE_CLASS = 0, + TRAINER_TYPE_MOUNTS = 1, // on blizz it's 2 + TRAINER_TYPE_TRADESKILLS = 2, + TRAINER_TYPE_PETS = 3 +}; + +#define MAX_TRAINER_TYPE 4 + +enum CreatureType +{ + CREATURE_TYPE_BEAST = 1, + CREATURE_TYPE_DRAGON = 2, + CREATURE_TYPE_DEMON = 3, + CREATURE_TYPE_ELEMENTAL = 4, + CREATURE_TYPE_GIANT = 5, + CREATURE_TYPE_UNDEAD = 6, + CREATURE_TYPE_HUMANOID = 7, + CREATURE_TYPE_CRITTER = 8, + CREATURE_TYPE_MECHANICAL = 9, + CREATURE_TYPE_NOTSPECIFIED = 10, + CREATURE_TYPE_TOTEM = 11, + CREATURE_TYPE_NON_COMBAT_PET = 12, + CREATURE_TYPE_GAS_CLOUD = 13 +}; + +uint32 const CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD = (1 << (CREATURE_TYPE_HUMANOID-1)) | (1 << (CREATURE_TYPE_UNDEAD-1)); + +enum CreatureFamily +{ + CREATURE_FAMILY_WOLF = 1, + CREATURE_FAMILY_CAT = 2, + CREATURE_FAMILY_SPIDER = 3, + CREATURE_FAMILY_BEAR = 4, + CREATURE_FAMILY_BOAR = 5, + CREATURE_FAMILY_CROCILISK = 6, + CREATURE_FAMILY_CARRION_BIRD = 7, + CREATURE_FAMILY_CRAB = 8, + CREATURE_FAMILY_GORILLA = 9, + CREATURE_FAMILY_RAPTOR = 11, + CREATURE_FAMILY_TALLSTRIDER = 12, + CREATURE_FAMILY_FELHUNTER = 15, + CREATURE_FAMILY_VOIDWALKER = 16, + CREATURE_FAMILY_SUCCUBUS = 17, + CREATURE_FAMILY_DOOMGUARD = 19, + CREATURE_FAMILY_SCORPID = 20, + CREATURE_FAMILY_TURTLE = 21, + CREATURE_FAMILY_IMP = 23, + CREATURE_FAMILY_BAT = 24, + CREATURE_FAMILY_HYENA = 25, + CREATURE_FAMILY_OWL = 26, + CREATURE_FAMILY_WIND_SERPENT = 27, + CREATURE_FAMILY_REMOTE_CONTROL = 28, + CREATURE_FAMILY_FELGUARD = 29, + CREATURE_FAMILY_DRAGONHAWK = 30, + CREATURE_FAMILY_RAVAGER = 31, + CREATURE_FAMILY_WARP_STALKER = 32, + CREATURE_FAMILY_SPOREBAT = 33, + CREATURE_FAMILY_NETHER_RAY = 34, + CREATURE_FAMILY_SERPENT = 35, + CREATURE_FAMILY_SEA_LION = 36 +}; + +enum CreatureEliteType +{ + CREATURE_ELITE_NORMAL = 0, + CREATURE_ELITE_ELITE = 1, + CREATURE_ELITE_RAREELITE = 2, + CREATURE_ELITE_WORLDBOSS = 3, + CREATURE_ELITE_RARE = 4, + CREATURE_UNKNOWN = 5 // found in 2.2.3 for 2 mobs +}; + +// values based at QuestInfo.dbc +enum QuestTypes +{ + QUEST_TYPE_ELITE = 1, + QUEST_TYPE_LIFE = 21, + QUEST_TYPE_PVP = 41, + QUEST_TYPE_RAID = 62, + QUEST_TYPE_DUNGEON = 81, + QUEST_TYPE_WORLD_EVENT = 82, + QUEST_TYPE_LEGENDARY = 83, + QUEST_TYPE_ESCORT = 84, + QUEST_TYPE_HEROIC = 85, +}; + +// values based at QuestSort.dbc +enum QuestSort +{ + QUEST_SORT_EPIC = 1, + QUEST_SORT_WAILING_CAVERNS_OLD = 21, + QUEST_SORT_SEASONAL = 22, + QUEST_SORT_UNDERCITY_OLD = 23, + QUEST_SORT_HERBALISM = 24, + QUEST_SORT_SCARLET_MONASTERY_OLD= 25, + QUEST_SORT_ULDAMN_OLD = 41, + QUEST_SORT_WARLOCK = 61, + QUEST_SORT_WARRIOR = 81, + QUEST_SORT_SHAMAN = 82, + QUEST_SORT_FISHING = 101, + QUEST_SORT_BLACKSMITHING = 121, + QUEST_SORT_PALADIN = 141, + QUEST_SORT_MAGE = 161, + QUEST_SORT_ROGUE = 162, + QUEST_SORT_ALCHEMY = 181, + QUEST_SORT_LEATHERWORKING = 182, + QUEST_SORT_ENGINERING = 201, + QUEST_SORT_TREASURE_MAP = 221, + QUEST_SORT_SUNKEN_TEMPLE_OLD = 241, + QUEST_SORT_HUNTER = 261, + QUEST_SORT_PRIEST = 262, + QUEST_SORT_DRUID = 263, + QUEST_SORT_TAILORING = 264, + QUEST_SORT_SPECIAL = 284, + QUEST_SORT_COOKING = 304, + QUEST_SORT_FIRST_AID = 324, + QUEST_SORT_LEGENDARY = 344, + QUEST_SORT_DARKMOON_FAIRE = 364, + QUEST_SORT_AHN_QIRAJ_WAR = 365, + QUEST_SORT_LUNAR_FESTIVAL = 366, + QUEST_SORT_REPUTATION = 367, + QUEST_SORT_INVASION = 368, + QUEST_SORT_MIDSUMMER = 369, + QUEST_SORT_BREWFEST = 370 +}; + +inline uint8 ClassByQuestSort(int32 QuestSort) +{ + switch(QuestSort) + { + case QUEST_SORT_WARLOCK: return CLASS_WARLOCK; + case QUEST_SORT_WARRIOR: return CLASS_WARRIOR; + case QUEST_SORT_SHAMAN: return CLASS_SHAMAN; + case QUEST_SORT_PALADIN: return CLASS_PALADIN; + case QUEST_SORT_MAGE: return CLASS_MAGE; + case QUEST_SORT_ROGUE: return CLASS_ROGUE; + case QUEST_SORT_HUNTER: return CLASS_HUNTER; + case QUEST_SORT_PRIEST: return CLASS_PRIEST; + case QUEST_SORT_DRUID: return CLASS_DRUID; + } + return 0; +} + +enum SkillType +{ + SKILL_FROST = 6, + SKILL_FIRE = 8, + SKILL_ARMS = 26, + SKILL_COMBAT = 38, + SKILL_SUBTLETY = 39, + SKILL_POISONS = 40, + SKILL_SWORDS = 43, + SKILL_AXES = 44, + SKILL_BOWS = 45, + SKILL_GUNS = 46, + SKILL_BEAST_MASTERY = 50, + SKILL_SURVIVAL = 51, + SKILL_MACES = 54, + SKILL_HOLY = 56, + SKILL_2H_SWORDS = 55, + SKILL_SHADOW = 78, + SKILL_DEFENSE = 95, + SKILL_LANG_COMMON = 98, + SKILL_RACIAL_DWARVEN = 101, + SKILL_LANG_ORCISH = 109, + SKILL_LANG_DWARVEN = 111, + SKILL_LANG_DARNASSIAN = 113, + SKILL_LANG_TAURAHE = 115, + SKILL_DUAL_WIELD = 118, + SKILL_RACIAL_TAUREN = 124, + SKILL_ORC_RACIAL = 125, + SKILL_RACIAL_NIGHT_ELF = 126, + SKILL_FIRST_AID = 129, + SKILL_FERAL_COMBAT = 134, + SKILL_STAVES = 136, + SKILL_LANG_THALASSIAN = 137, + SKILL_LANG_DRACONIC = 138, + SKILL_LANG_DEMON_TONGUE = 139, + SKILL_LANG_TITAN = 140, + SKILL_LANG_OLD_TONGUE = 141, + SKILL_SURVIVAL2 = 142, + SKILL_RIDING_HORSE = 148, + SKILL_RIDING_WOLF = 149, + SKILL_RIDING_RAM = 152, + SKILL_RIDING_TIGER = 150, + SKILL_SWIMING = 155, + SKILL_2H_MACES = 160, + SKILL_UNARMED = 162, + SKILL_MARKSMANSHIP = 163, + SKILL_BLACKSMITHING = 164, + SKILL_LEATHERWORKING = 165, + SKILL_ALCHEMY = 171, + SKILL_2H_AXES = 172, + SKILL_DAGGERS = 173, + SKILL_THROWN = 176, + SKILL_HERBALISM = 182, + SKILL_GENERIC_DND = 183, + SKILL_RETRIBUTION = 184, + SKILL_COOKING = 185, + SKILL_MINING = 186, + SKILL_PET_IMP = 188, + SKILL_PET_FELHUNTER = 189, + SKILL_TAILORING = 197, + SKILL_ENGINERING = 202, + SKILL_PET_SPIDER = 203, + SKILL_PET_VOIDWALKER = 204, + SKILL_PET_SUCCUBUS = 205, + SKILL_PET_INFERNAL = 206, + SKILL_PET_DOOMGUARD = 207, + SKILL_PET_WOLF = 208, + SKILL_PET_CAT = 209, + SKILL_PET_BEAR = 210, + SKILL_PET_BOAR = 211, + SKILL_PET_CROCILISK = 212, + SKILL_PET_CARRION_BIRD = 213, + SKILL_PET_GORILLA = 215, + SKILL_PET_CRAB = 214, + SKILL_PET_RAPTOR = 217, + SKILL_PET_TALLSTRIDER = 218, + SKILL_RACIAL_UNDED = 220, + SKILL_WEAPON_TALENTS = 222, + SKILL_CROSSBOWS = 226, + SKILL_SPEARS = 227, + SKILL_WANDS = 228, + SKILL_POLEARMS = 229, + SKILL_PET_SCORPID = 236, + SKILL_ARCANE = 237, + SKILL_OPEN_LOCK = 242, + SKILL_PET_TURTLE = 251, + SKILL_ASSASSINATION = 253, + SKILL_FURY = 256, + SKILL_PROTECTION = 257, + SKILL_BEAST_TRAINING = 261, + SKILL_PROTECTION2 = 267, + SKILL_PET_TALENTS = 270, + SKILL_PLATE_MAIL = 293, + SKILL_LANG_GNOMISH = 313, + SKILL_LANG_TROLL = 315, + SKILL_ENCHANTING = 333, + SKILL_DEMONOLOGY = 354, + SKILL_AFFLICTION = 355, + SKILL_FISHING = 356, + SKILL_ENHANCEMENT = 373, + SKILL_RESTORATION = 374, + SKILL_ELEMENTAL_COMBAT = 375, + SKILL_SKINNING = 393, + SKILL_MAIL = 413, + SKILL_LEATHER = 414, + SKILL_CLOTH = 415, + SKILL_SHIELD = 433, + SKILL_FIST_WEAPONS = 473, + SKILL_RIDING_RAPTOR = 533, + SKILL_RIDING_MECHANOSTRIDER = 553, + SKILL_RIDING_UNDEAD_HORSE = 554, + SKILL_RESTORATION2 = 573, + SKILL_BALANCE = 574, + SKILL_DESTRUCTION = 593, + SKILL_HOLY2 = 594, + SKILL_DISCIPLINE = 613, + SKILL_LOCKPICKING = 633, + SKILL_PET_BAT = 653, + SKILL_PET_HYENA = 654, + SKILL_PET_OWL = 655, + SKILL_PET_WIND_SERPENT = 656, + SKILL_LANG_GUTTERSPEAK = 673, + SKILL_RIDING_KODO = 713, + SKILL_RACIAL_TROLL = 733, + SKILL_RACIAL_GNOME = 753, + SKILL_RACIAL_HUMAN = 754, + SKILL_JEWELCRAFTING = 755, + SKILL_RACIAL_BLOODELF = 756, + SKILL_PET_EVENT_RC = 758, + SKILL_LANG_DRAENEI = 759, + SKILL_RACIAL_DRAENEI = 760, + SKILL_PET_FELGUARD = 761, + SKILL_RIDING = 762, + SKILL_PET_DRAGONHAWK = 763, + SKILL_PET_NETHER_RAY = 764, + SKILL_PET_SPOREBAT = 765, + SKILL_PET_WARP_STALKER = 766, + SKILL_PET_RAVAGER = 767, + SKILL_PET_SERPENT = 768, + SKILL_INTERNAL = 769 +}; + +#define MAX_SKILL_TYPE 770 + +inline uint32 SkillByQuestSort(int32 QuestSort) +{ + switch(QuestSort) + { + case QUEST_SORT_HERBALISM: return SKILL_HERBALISM; + case QUEST_SORT_FISHING: return SKILL_FISHING; + case QUEST_SORT_BLACKSMITHING: return SKILL_BLACKSMITHING; + case QUEST_SORT_ALCHEMY: return SKILL_ALCHEMY; + case QUEST_SORT_LEATHERWORKING: return SKILL_LEATHERWORKING; + case QUEST_SORT_ENGINERING: return SKILL_ENGINERING; + case QUEST_SORT_TAILORING: return SKILL_TAILORING; + case QUEST_SORT_COOKING: return SKILL_COOKING; + case QUEST_SORT_FIRST_AID: return SKILL_FIRST_AID; + } + return 0; +} + +enum SkillCategory +{ + SKILL_CATEGORY_ATTRIBUTES = 5, + SKILL_CATEGORY_WEAPON = 6, + SKILL_CATEGORY_CLASS = 7, + SKILL_CATEGORY_ARMOR = 8, + SKILL_CATEGORY_SECONDARY = 9, // secondary professions + SKILL_CATEGORY_LANGUAGES = 10, + SKILL_CATEGORY_PROFESSION = 11, // primary professions + SKILL_CATEGORY_NOT_DISPLAYED = 12 +}; + +enum TotemCategory +{ + TC_SKINNING_SKIFE = 1, + TC_EARTH_TOTEM = 2, + TC_AIR_TOTEM = 3, + TC_FIRE_TOTEM = 4, + TC_WATER_TOTEM = 5, + TC_COPPER_ROD = 6, + TC_SILVER_ROD = 7, + TC_GOLDEN_ROD = 8, + TC_TRUESILVER_ROD = 9, + TC_ARCANITE_ROD = 10, + TC_MINING_PICK = 11, + TC_PHILOSOPHERS_STONE = 12, + TC_BLACKSMITH_HAMMER = 13, + TC_ARCLIGHT_SPANNER = 14, + TC_GYROMATIC_MA = 15, + TC_MASTER_TOTEM = 21, + TC_FEL_IRON_ROD = 41, + TC_ADAMANTITE_ROD = 62, + TC_ETERNIUM_ROD = 63 +}; + +enum UnitDynFlags +{ + UNIT_DYNFLAG_LOOTABLE = 0x0001, + UNIT_DYNFLAG_TRACK_UNIT = 0x0002, + UNIT_DYNFLAG_OTHER_TAGGER = 0x0004, + UNIT_DYNFLAG_ROOTED = 0x0008, + UNIT_DYNFLAG_SPECIALINFO = 0x0010, + UNIT_DYNFLAG_DEAD = 0x0020 +}; + +enum CorpseDynFlags +{ + CORPSE_DYNFLAG_LOOTABLE = 0x0001 +}; + +// Passive Spell codes explicit used in code +#define SPELL_ID_GENERIC_LEARN 483 +#define SPELL_ID_PASSIVE_BATTLE_STANCE 2457 +#define SPELL_ID_PASSIVE_RESURRECTION_SICKNESS 15007 + +enum WeatherType +{ + WEATHER_TYPE_FINE = 0, + WEATHER_TYPE_RAIN = 1, + WEATHER_TYPE_SNOW = 2, + WEATHER_TYPE_STORM = 3, + WEATHER_TYPE_THUNDERS = 86, + WEATHER_TYPE_BLACKRAIN = 90 +}; + +#define MAX_WEATHER_TYPE 4 + +enum ChatMsg +{ + CHAT_MSG_ADDON = 0xFFFFFFFF, + CHAT_MSG_SYSTEM = 0x00, + CHAT_MSG_SAY = 0x01, + CHAT_MSG_PARTY = 0x02, + CHAT_MSG_RAID = 0x03, + CHAT_MSG_GUILD = 0x04, + CHAT_MSG_OFFICER = 0x05, + CHAT_MSG_YELL = 0x06, + CHAT_MSG_WHISPER = 0x07, + CHAT_MSG_WHISPER_INFORM = 0x08, + CHAT_MSG_REPLY = 0x09, + CHAT_MSG_EMOTE = 0x0A, + CHAT_MSG_TEXT_EMOTE = 0x0B, + CHAT_MSG_MONSTER_SAY = 0x0C, + CHAT_MSG_MONSTER_PARTY = 0x0D, + CHAT_MSG_MONSTER_YELL = 0x0E, + CHAT_MSG_MONSTER_WHISPER = 0x0F, + CHAT_MSG_MONSTER_EMOTE = 0x10, + CHAT_MSG_CHANNEL = 0x11, + CHAT_MSG_CHANNEL_JOIN = 0x12, + CHAT_MSG_CHANNEL_LEAVE = 0x13, + CHAT_MSG_CHANNEL_LIST = 0x14, + CHAT_MSG_CHANNEL_NOTICE = 0x15, + CHAT_MSG_CHANNEL_NOTICE_USER = 0x16, + CHAT_MSG_AFK = 0x17, + CHAT_MSG_DND = 0x18, + CHAT_MSG_IGNORED = 0x19, + CHAT_MSG_SKILL = 0x1A, + CHAT_MSG_LOOT = 0x1B, + CHAT_MSG_MONEY = 0x1C, + CHAT_MSG_OPENING = 0x1D, + CHAT_MSG_TRADESKILLS = 0x1E, + CHAT_MSG_PET_INFO = 0x1F, + CHAT_MSG_COMBAT_MISC_INFO = 0x20, + CHAT_MSG_COMBAT_XP_GAIN = 0x21, + CHAT_MSG_COMBAT_HONOR_GAIN = 0x22, + CHAT_MSG_COMBAT_FACTION_CHANGE = 0x23, + CHAT_MSG_BG_SYSTEM_NEUTRAL = 0x24, + CHAT_MSG_BG_SYSTEM_ALLIANCE = 0x25, + CHAT_MSG_BG_SYSTEM_HORDE = 0x26, + CHAT_MSG_RAID_LEADER = 0x27, + CHAT_MSG_RAID_WARNING = 0x28, + CHAT_MSG_RAID_BOSS_WHISPER = 0x29, + CHAT_MSG_RAID_BOSS_EMOTE = 0x2A, + CHAT_MSG_FILTERED = 0x2B, + CHAT_MSG_BATTLEGROUND = 0x2C, + CHAT_MSG_BATTLEGROUND_LEADER = 0x2D, + CHAT_MSG_RESTRICTED = 0x2E, +}; + +#define MAX_CHAT_MSG_TYPE 0x2F + +// Values from ItemPetFood (power of (value-1) used for compare with CreatureFamilyEntry.petDietMask +enum PetDiet +{ + PET_DIET_MEAT = 1, + PET_DIET_FISH = 2, + PET_DIET_CHEESE = 3, + PET_DIET_BREAD = 4, + PET_DIET_FUNGAS = 5, + PET_DIET_FRUIT = 6, + PET_DIET_RAW_MEAT = 7, + PET_DIET_RAW_FISH = 8 +}; + +#define MAX_PET_DIET 9 + +#define CHAIN_SPELL_JUMP_RADIUS 10 + +// Max values for Guild & Guild Bank +#define GUILD_BANK_MAX_TABS 6 +#define GUILD_BANK_MAX_SLOTS 98 +#define GUILD_BANK_MAX_LOGS 24 +#define GUILD_EVENTLOG_MAX_ENTRIES 100 +#define GUILD_MAX_RANKS 10 + +enum AiReaction +{ + AI_REACTION_UNK1 = 1, + AI_REACTION_AGGRO = 2, + AI_REACTION_UNK3 = 3, + AI_REACTION_UNK4 = 4 +}; + +// Diminishing Returns Types +enum DiminishingReturnsType +{ + DRTYPE_NONE = 0, // this spell is not diminished, but may have limited it's duration to 10s + DRTYPE_PLAYER = 1, // this spell is diminished only when applied on players + DRTYPE_ALL = 2 // this spell is diminished in every case +}; + +// Diminishing Return Groups +enum DiminishingGroup +{ + // Common Groups + DIMINISHING_NONE, + DIMINISHING_CONTROL_STUN, // Player Controlled stuns + DIMINISHING_TRIGGER_STUN, // By aura proced stuns, usualy chance on hit talents + DIMINISHING_SLEEP, + DIMINISHING_CONTROL_ROOT, // Immobilizing effects from casted spells + DIMINISHING_TRIGGER_ROOT, // Immobilizing effects from triggered spells like Frostbite + DIMINISHING_FEAR, // Non-warlock fears + DIMINISHING_CHARM, + // Mage Specific + DIMINISHING_POLYMORPH, + // Rogue Specific + DIMINISHING_KIDNEYSHOT, // Kidney Shot is not diminished with Cheap Shot + // Warlock Specific + DIMINISHING_DEATHCOIL, // Death Coil Diminish only with another Death Coil + DIMINISHING_WARLOCK_FEAR, // Also with Sedduction + // Shared Class Specific + DIMINISHING_BLIND_CYCLONE, // From 2.3.0 + DIMINISHING_DISARM, // From 2.3.0 + DIMINISHING_SILENCE, // From 2.3.0 + DIMINISHING_FREEZE, // Hunter's Freezing Trap + DIMINISHING_KNOCKOUT, // Also with Sap, all Knockout mechanics are here + DIMINISHING_BANISH, + // Other + // Don't Diminish, but limit duration to 10s + DIMINISHING_LIMITONLY +}; + +enum DungeonDifficulties +{ + DIFFICULTY_NORMAL = 0, + DIFFICULTY_HEROIC = 1, + TOTAL_DIFFICULTIES +}; + +enum SummonType +{ + SUMMON_TYPE_CRITTER = 41, + SUMMON_TYPE_GUARDIAN = 61, + SUMMON_TYPE_TOTEM_SLOT1 = 63, + SUMMON_TYPE_WILD = 64, + SUMMON_TYPE_POSESSED = 65, + SUMMON_TYPE_DEMON = 66, + SUMMON_TYPE_SUMMON = 67, + SUMMON_TYPE_TOTEM_SLOT2 = 81, + SUMMON_TYPE_TOTEM_SLOT3 = 82, + SUMMON_TYPE_TOTEM_SLOT4 = 83, + SUMMON_TYPE_TOTEM = 121, + SUMMON_TYPE_UNKNOWN3 = 181, + SUMMON_TYPE_UNKNOWN4 = 187, + SUMMON_TYPE_UNKNOWN1 = 247, + SUMMON_TYPE_UNKNOWN5 = 307, + SUMMON_TYPE_CRITTER2 = 407, + SUMMON_TYPE_UNKNOWN6 = 409, + SUMMON_TYPE_UNKNOWN2 = 427, + SUMMON_TYPE_POSESSED2 = 428 +}; +#endif diff --git a/src/game/SkillDiscovery.cpp b/src/game/SkillDiscovery.cpp new file mode 100644 index 00000000000..388d06ad184 --- /dev/null +++ b/src/game/SkillDiscovery.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Database/DatabaseEnv.h" +#include "Log.h" +#include "ProgressBar.h" +#include "Policies/SingletonImp.h" +#include "ObjectAccessor.h" +#include "World.h" +#include "Util.h" +#include "SkillDiscovery.h" +#include "SpellMgr.h" +#include + +struct SkillDiscoveryEntry +{ + uint32 spellId; + float chance; + + SkillDiscoveryEntry() + : spellId(0), chance(0) {} + + SkillDiscoveryEntry(uint16 _spellId, float _chance) + : spellId(_spellId), chance(_chance) {} +}; + +typedef std::list SkillDiscoveryList; +typedef HM_NAMESPACE::hash_map SkillDiscoveryMap; + +static SkillDiscoveryMap SkillDiscoveryStore; + +void LoadSkillDiscoveryTable() +{ + + SkillDiscoveryStore.clear(); // need for reload + + uint32 count = 0; + + // 0 1 2 + QueryResult *result = WorldDatabase.PQuery("SELECT spellId, reqSpell, chance FROM skill_discovery_template"); + + if (result) + { + barGoLink bar(result->GetRowCount()); + + std::ostringstream ssNonDiscoverableEntries; + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 spellId = fields[0].GetUInt32(); + int32 reqSkillOrSpell = fields[1].GetInt32(); + float chance = fields[2].GetFloat(); + + if( chance <= 0 ) // chance + { + ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell << " chance = " << chance << "\n"; + continue; + } + + if(reqSkillOrSpell > 0) // spell case + { + SpellEntry const* spellEntry = sSpellStore.LookupEntry(reqSkillOrSpell); + if( !spellEntry ) + { + sLog.outErrorDb("Spell (ID: %u) have not existed spell (ID: %i) in `reqSpell` field in `skill_discovery_template` table",spellId,reqSkillOrSpell); + continue; + } + + if( spellEntry->Mechanic != MECHANIC_DISCOVERY ) + { + sLog.outErrorDb("Spell (ID: %u) not have have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc but listed in `skill_discovery_template` table",spellId); + continue; + } + + SkillDiscoveryStore[reqSkillOrSpell].push_back( SkillDiscoveryEntry(spellId, chance) ); + } + else if( reqSkillOrSpell == 0 ) // skill case + { + SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellId); + SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellId); + + if(lower==upper) + { + sLog.outErrorDb("Spell (ID: %u) not listed in `SkillLineAbility.dbc` but listed with `reqSpell`=0 in `skill_discovery_template` table",spellId); + continue; + } + + for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) + { + SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back( SkillDiscoveryEntry(spellId, chance) ); + } + } + else + { + sLog.outErrorDb("Spell (ID: %u) have negative value in `reqSpell` field in `skill_discovery_template` table",spellId); + continue; + } + ++count; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u skill discovery definitions", count ); + if(!ssNonDiscoverableEntries.str().empty()) + sLog.outErrorDb("Some items can't be successfully discovered: have in chance field value < 0.000001 in `skill_discovery_template` DB table . List:\n%s",ssNonDiscoverableEntries.str().c_str()); + } + else + { + sLog.outString(); + sLog.outString( ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty." ); + } +} + +uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player) +{ + // check spell case + SkillDiscoveryMap::iterator tab = SkillDiscoveryStore.find(spellId); + + if(tab != SkillDiscoveryStore.end()) + { + for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) + { + if( roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY)) + && !player->HasSpell(item_iter->spellId) ) + return item_iter->spellId; + } + + return 0; + } + + // check skill line case + tab = SkillDiscoveryStore.find(-(int32)skillId); + if(tab != SkillDiscoveryStore.end()) + { + for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) + { + if( roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY)) + && !player->HasSpell(item_iter->spellId) ) + return item_iter->spellId; + } + + return 0; + } + + return 0; +} diff --git a/src/game/SkillDiscovery.h b/src/game/SkillDiscovery.h new file mode 100644 index 00000000000..6a7a16c3849 --- /dev/null +++ b/src/game/SkillDiscovery.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_SKILLDISCOVERY_H +#define MANGOS_SKILLDISCOVERY_H + +#include "Common.h" + +class Player; + +void LoadSkillDiscoveryTable(); +uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player); +#endif diff --git a/src/game/SkillExtraItems.cpp b/src/game/SkillExtraItems.cpp new file mode 100644 index 00000000000..42e465be41d --- /dev/null +++ b/src/game/SkillExtraItems.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SkillExtraItems.h" +#include "Database/DatabaseEnv.h" +#include "Log.h" +#include "ProgressBar.h" +#include "Player.h" +#include + +// some type definitions +// no use putting them in the header file, they're only used in this .cpp + +// struct to store information about extra item creation +// one entry for every spell that is able to create an extra item +struct SkillExtraItemEntry +{ + // the spell id of the specialization required to create extra items + uint32 requiredSpecialization; + // the chance to create one additional item + float additionalCreateChance; + // maximum number of extra items created per crafting + uint8 additionalMaxNum; + + SkillExtraItemEntry() + : requiredSpecialization(0), additionalCreateChance(0.0f), additionalMaxNum(0) {} + + SkillExtraItemEntry(uint32 rS, float aCC, uint8 aMN) + : requiredSpecialization(rS), additionalCreateChance(aCC), additionalMaxNum(aMN) {} +}; + +// map to store the extra item creation info, the key is the spellId of the creation spell, the mapped value is the assigned SkillExtraItemEntry +typedef std::map SkillExtraItemMap; + +SkillExtraItemMap SkillExtraItemStore; + +// loads the extra item creation info from DB +void LoadSkillExtraItemTable() +{ + uint32 count = 0; + + SkillExtraItemStore.clear(); // need for reload + + // 0 1 2 3 + QueryResult *result = WorldDatabase.PQuery("SELECT spellId, requiredSpecialization, additionalCreateChance, additionalMaxNum FROM skill_extra_item_template"); + + if (result) + { + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 spellId = fields[0].GetUInt32(); + + if(!sSpellStore.LookupEntry(spellId)) + { + sLog.outError("Skill specialization %u has non-existent spell id in `skill_extra_item_template`!", spellId); + continue; + } + + uint32 requiredSpecialization = fields[1].GetUInt32(); + if(!sSpellStore.LookupEntry(requiredSpecialization)) + { + sLog.outError("Skill specialization %u have not existed required specialization spell id %u in `skill_extra_item_template`!", spellId,requiredSpecialization); + continue; + } + + float additionalCreateChance = fields[2].GetFloat(); + if(additionalCreateChance <= 0.0f) + { + sLog.outError("Skill specialization %u has too low additional create chance in `skill_extra_item_template`!", spellId); + continue; + } + + uint8 additionalMaxNum = fields[3].GetUInt8(); + if(!additionalMaxNum) + { + sLog.outError("Skill specialization %u has 0 max number of extra items in `skill_extra_item_template`!", spellId); + continue; + } + + SkillExtraItemEntry& skillExtraItemEntry = SkillExtraItemStore[spellId]; + + skillExtraItemEntry.requiredSpecialization = requiredSpecialization; + skillExtraItemEntry.additionalCreateChance = additionalCreateChance; + skillExtraItemEntry.additionalMaxNum = additionalMaxNum; + + ++count; + } while (result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u spell specialization definitions", count ); + } + else + { + sLog.outString(); + sLog.outString( ">> Loaded 0 spell specialization definitions. DB table `skill_extra_item_template` is empty." ); + } +} + +bool canCreateExtraItems(Player * player, uint32 spellId, float &additionalChance, uint8 &additionalMax) +{ + // get the info for the specified spell + SkillExtraItemMap::const_iterator ret = SkillExtraItemStore.find(spellId); + if(ret==SkillExtraItemStore.end()) + return false; + + SkillExtraItemEntry const* specEntry = &ret->second; + + // if no entry, then no extra items can be created + if(!specEntry) + return false; + + // the player doesn't have the required specialization, return false + if(!player->HasSpell(specEntry->requiredSpecialization)) + return false; + + // set the arguments to the appropriate values + additionalChance = specEntry->additionalCreateChance; + additionalMax = specEntry->additionalMaxNum; + + // enable extra item creation + return true; +} diff --git a/src/game/SkillExtraItems.h b/src/game/SkillExtraItems.h new file mode 100644 index 00000000000..51d34da40dd --- /dev/null +++ b/src/game/SkillExtraItems.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_SKILL_EXTRA_ITEMS_H +#define MANGOS_SKILL_EXTRA_ITEMS_H + +#include "Common.h" + +// predef classes used in functions +class Player; +// returns true and sets the appropriate info if the player can create extra items with the given spellId +bool canCreateExtraItems(Player * player, uint32 spellId, float &additionalChance, uint8 &additionalMax); +// function to load the extra item creation info from DB +void LoadSkillExtraItemTable(); +#endif diff --git a/src/game/SkillHandler.cpp b/src/game/SkillHandler.cpp new file mode 100644 index 00000000000..18016018ecd --- /dev/null +++ b/src/game/SkillHandler.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "Opcodes.h" +#include "Log.h" +#include "Player.h" +#include "World.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "ObjectAccessor.h" +#include "UpdateMask.h" +#include "SpellAuras.h" + +void WorldSession::HandleLearnTalentOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,4+4); + + uint32 talent_id, requested_rank; + recv_data >> talent_id >> requested_rank; + + uint32 CurTalentPoints = GetPlayer()->GetFreeTalentPoints(); + + if(CurTalentPoints == 0) + return; + + if (requested_rank > 4) + return; + + TalentEntry const *talentInfo = sTalentStore.LookupEntry( talent_id ); + + if(!talentInfo) + return; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); + + if(!talentTabInfo) + return; + + Player * player = GetPlayer(); + + // prevent learn talent for different class (cheating) + if( (player->getClassMask() & talentTabInfo->ClassMask) == 0 ) + return; + + // prevent skip talent ranks (cheating) + if(requested_rank > 0 && !player->HasSpell(talentInfo->RankID[requested_rank-1])) + return; + + // Check if it requires another talent + if (talentInfo->DependsOn > 0) + { + if(TalentEntry const *depTalentInfo = sTalentStore.LookupEntry(talentInfo->DependsOn)) + { + bool hasEnoughRank = false; + for (int i = talentInfo->DependsOnRank; i <= 4; i++) + { + if (depTalentInfo->RankID[i] != 0) + if (player->HasSpell(depTalentInfo->RankID[i])) + hasEnoughRank = true; + } + if (!hasEnoughRank) + return; + } + } + + // Check if it requires spell + if( talentInfo->DependsOnSpell && !player->HasSpell(talentInfo->DependsOnSpell) ) + return; + + // Find out how many points we have in this field + uint32 spentPoints = 0; + + uint32 tTab = talentInfo->TalentTab; + if (talentInfo->Row > 0) + { + unsigned int numRows = sTalentStore.GetNumRows(); + for (unsigned int i = 0; i < numRows; i++) // Loop through all talents. + { + // Someday, someone needs to revamp + const TalentEntry *tmpTalent = sTalentStore.LookupEntry(i); + if (tmpTalent) // the way talents are tracked + { + if (tmpTalent->TalentTab == tTab) + { + for (int j = 0; j <= 4; j++) + { + if (tmpTalent->RankID[j] != 0) + { + if (player->HasSpell(tmpTalent->RankID[j])) + { + spentPoints += j + 1; + } + } + } + } + } + } + } + + // not have required min points spent in talent tree + if(spentPoints < (talentInfo->Row * 5)) + return; + + // spell not set in talent.dbc + uint32 spellid = talentInfo->RankID[requested_rank]; + if( spellid == 0 ) + { + sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talent_id, requested_rank); + return; + } + + // already known + if(GetPlayer( )->HasSpell(spellid)) + return; + + // learn! (other talent ranks will unlearned at learning) + GetPlayer( )->learnSpell(spellid); + sLog.outDetail("TalentID: %u Rank: %u Spell: %u\n", talent_id, requested_rank, spellid); + + // update free talent points + GetPlayer()->SetFreeTalentPoints(CurTalentPoints - 1); +} + +void WorldSession::HandleTalentWipeOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + sLog.outDetail("MSG_TALENT_WIPE_CONFIRM"); + uint64 guid; + recv_data >> guid; + + Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid,UNIT_NPC_FLAG_TRAINER); + if (!unit) + { + sLog.outDebug( "WORLD: HandleTalentWipeOpcode - 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(!(_player->resetTalents())) + { + WorldPacket data( MSG_TALENT_WIPE_CONFIRM, 8+4); //you have not any talent + data << uint64(0); + data << uint32(0); + SendPacket( &data ); + return; + } + + unit->CastSpell(_player, 14867, true); //spell: "Untalent Visual Effect" +} + +void WorldSession::HandleUnlearnSkillOpcode(WorldPacket & recv_data) +{ + CHECK_PACKET_SIZE(recv_data,4); + + uint32 skill_id; + recv_data >> skill_id; + GetPlayer()->SetSkill(skill_id, 0, 0); +} diff --git a/src/game/SocialMgr.cpp b/src/game/SocialMgr.cpp new file mode 100644 index 00000000000..85afb48108a --- /dev/null +++ b/src/game/SocialMgr.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SocialMgr.h" +#include "Policies/SingletonImp.h" +#include "Database/DatabaseEnv.h" +#include "Opcodes.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Player.h" +#include "ObjectMgr.h" +#include "World.h" +#include "Util.h" + +INSTANTIATE_SINGLETON_1( SocialMgr ); + +PlayerSocial::PlayerSocial() +{ + m_playerGUID = 0; +} + +PlayerSocial::~PlayerSocial() +{ + m_playerSocialMap.clear(); +} + +bool PlayerSocial::AddToSocialList(uint32 friend_guid, bool ignore) +{ + // prevent list (client-side) overflow + if(m_playerSocialMap.size() >= (255-1)) + return false; + + uint32 flag = SOCIAL_FLAG_FRIEND; + if(ignore) + flag = SOCIAL_FLAG_IGNORED; + + PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid); + if(itr != m_playerSocialMap.end()) + { + CharacterDatabase.PExecute("UPDATE character_social SET flags = (flags | %u) WHERE guid = '%u' AND friend = '%u'", flag, GetPlayerGUID(), friend_guid); + m_playerSocialMap[friend_guid].Flags |= flag; + } + else + { + CharacterDatabase.PExecute("INSERT INTO character_social (guid, friend, flags) VALUES ('%u', '%u', '%u')", GetPlayerGUID(), friend_guid, flag); + FriendInfo fi; + fi.Flags |= flag; + m_playerSocialMap[friend_guid] = fi; + } + return true; +} + +void PlayerSocial::RemoveFromSocialList(uint32 friend_guid, bool ignore) +{ + PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid); + if(itr == m_playerSocialMap.end()) // not exist + return; + + uint32 flag = SOCIAL_FLAG_FRIEND; + if(ignore) + flag = SOCIAL_FLAG_IGNORED; + + itr->second.Flags &= ~flag; + if(itr->second.Flags == 0) + { + CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' AND friend = '%u'", GetPlayerGUID(), friend_guid); + m_playerSocialMap.erase(itr); + } + else + { + CharacterDatabase.PExecute("UPDATE character_social SET flags = (flags & ~%u) WHERE guid = '%u' AND friend = '%u'", flag, GetPlayerGUID(), friend_guid); + } +} + +void PlayerSocial::SetFriendNote(uint32 friend_guid, std::string note) +{ + PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid); + if(itr == m_playerSocialMap.end()) // not exist + return; + + utf8truncate(note,48); // DB and client size limitation + + CharacterDatabase.escape_string(note); + CharacterDatabase.PExecute("UPDATE character_social SET note = '%s' WHERE guid = '%u' AND friend = '%u'", note.c_str(), GetPlayerGUID(), friend_guid); + m_playerSocialMap[friend_guid].Note = note; +} + +void PlayerSocial::SendSocialList() +{ + Player *plr = objmgr.GetPlayer(GetPlayerGUID()); + if(!plr) + return; + + uint32 size = m_playerSocialMap.size(); + + WorldPacket data(SMSG_CONTACT_LIST, (4+4+size*25)); // just can guess size + data << uint32(7); // unk flag (0x1, 0x2, 0x4), 0x7 if it include ignore list + data << uint32(size); // friends count + + for(PlayerSocialMap::iterator itr = m_playerSocialMap.begin(); itr != m_playerSocialMap.end(); ++itr) + { + sSocialMgr.GetFriendInfo(plr, itr->first, itr->second); + + data << uint64(itr->first); // player guid + data << uint32(itr->second.Flags); // player flag (0x1-friend?, 0x2-ignored?, 0x4-muted?) + data << itr->second.Note; // string note + if(itr->second.Flags & SOCIAL_FLAG_FRIEND) // if IsFriend() + { + data << uint8(itr->second.Status); // online/offline/etc? + if(itr->second.Status) // if online + { + data << uint32(itr->second.Area); // player area + data << uint32(itr->second.Level); // player level + data << uint32(itr->second.Class); // player class + } + } + } + + plr->GetSession()->SendPacket(&data); + sLog.outDebug("WORLD: Sent SMSG_CONTACT_LIST"); +} + +bool PlayerSocial::HasFriend(uint32 friend_guid) +{ + PlayerSocialMap::iterator itr = m_playerSocialMap.find(friend_guid); + if(itr != m_playerSocialMap.end()) + return itr->second.Flags & SOCIAL_FLAG_FRIEND; + return false; +} + +bool PlayerSocial::HasIgnore(uint32 ignore_guid) +{ + PlayerSocialMap::iterator itr = m_playerSocialMap.find(ignore_guid); + if(itr != m_playerSocialMap.end()) + return itr->second.Flags & SOCIAL_FLAG_IGNORED; + return false; +} + +SocialMgr::SocialMgr() +{ + +} + +SocialMgr::~SocialMgr() +{ + +} + +void SocialMgr::RemovePlayerSocial(uint32 guid) +{ + SocialMap::iterator itr = m_socialMap.find(guid); + if(itr != m_socialMap.end()) + m_socialMap.erase(itr); +} + +void SocialMgr::GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &friendInfo) +{ + if(!player) + return; + + Player *pFriend = ObjectAccessor::FindPlayer(friendGUID); + + uint32 team = player->GetTeam(); + uint32 security = player->GetSession()->GetSecurity(); + bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); + bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST) || security > SEC_PLAYER; + + // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters + // MODERATOR, GAME MASTER, ADMINISTRATOR can see all + if( pFriend && pFriend->GetName() && + ( security > SEC_PLAYER || + ( pFriend->GetTeam() == team || allowTwoSideWhoList ) && + ( pFriend->GetSession()->GetSecurity() == SEC_PLAYER || gmInWhoList && pFriend->IsVisibleGloballyFor(player) ))) + { + friendInfo.Status = FRIEND_STATUS_ONLINE; + if(pFriend->isAFK()) + friendInfo.Status = FRIEND_STATUS_AFK; + if(pFriend->isDND()) + friendInfo.Status = FRIEND_STATUS_DND; + friendInfo.Area = pFriend->GetZoneId(); + friendInfo.Level = pFriend->getLevel(); + friendInfo.Class = pFriend->getClass(); + } + else + { + friendInfo.Status = FRIEND_STATUS_OFFLINE; + friendInfo.Area = 0; + friendInfo.Level = 0; + friendInfo.Class = 0; + } +} + +void SocialMgr::MakeFriendStatusPacket(FriendsResult result, uint32 guid, WorldPacket *data) +{ + data->Initialize(SMSG_FRIEND_STATUS, 5); + *data << uint8(result); + *data << uint64(guid); +} + +void SocialMgr::SendFriendStatus(Player *player, FriendsResult result, uint32 friend_guid, std::string name, bool broadcast) +{ + FriendInfo fi; + + WorldPacket data; + MakeFriendStatusPacket(result, friend_guid, &data); + switch(result) + { + case FRIEND_ONLINE: + GetFriendInfo(player, friend_guid, fi); + data << uint8(fi.Status); + data << uint32(fi.Area); + data << uint32(fi.Level); + data << uint32(fi.Class); + break; + case FRIEND_ADDED_ONLINE: + GetFriendInfo(player, friend_guid, fi); + data << name; + data << uint8(fi.Status); + data << uint32(fi.Area); + data << uint32(fi.Level); + data << uint32(fi.Class); + break; + case FRIEND_ADDED_OFFLINE: + data << name; + break; + } + + if(broadcast) + BroadcastToFriendListers(player, &data); + else + player->GetSession()->SendPacket(&data); +} + +void SocialMgr::BroadcastToFriendListers(Player *player, WorldPacket *packet) +{ + if(!player) + return; + + uint32 team = player->GetTeam(); + uint32 security = player->GetSession()->GetSecurity(); + uint32 guid = player->GetGUIDLow(); + bool gmInWhoList = sWorld.getConfig(CONFIG_GM_IN_WHO_LIST); + bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); + + for(SocialMap::iterator itr = m_socialMap.begin(); itr != m_socialMap.end(); ++itr) + { + PlayerSocialMap::iterator itr2 = itr->second.m_playerSocialMap.find(guid); + if(itr2 != itr->second.m_playerSocialMap.end() && (itr2->second.Flags & SOCIAL_FLAG_FRIEND)) + { + Player *pFriend = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)); + + // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters + // MODERATOR, GAME MASTER, ADMINISTRATOR can see all + if( pFriend && pFriend->IsInWorld() && + ( pFriend->GetSession()->GetSecurity() > SEC_PLAYER || + ( pFriend->GetTeam() == team || allowTwoSideWhoList ) && + (security == SEC_PLAYER || gmInWhoList && player->IsVisibleGloballyFor(pFriend) ))) + { + pFriend->GetSession()->SendPacket(packet); + } + } + } +} + +PlayerSocial *SocialMgr::LoadFromDB(QueryResult *result, uint32 guid) +{ + PlayerSocial *social = &m_socialMap[guid]; + social->SetPlayerGUID(guid); + + if(!result) + return social; + + uint32 friend_guid = 0; + uint32 flags = 0; + std::string note = ""; + + do + { + Field *fields = result->Fetch(); + + friend_guid = fields[0].GetUInt32(); + flags = fields[1].GetUInt32(); + note = fields[2].GetCppString(); + + social->m_playerSocialMap[friend_guid] = FriendInfo(flags, note); + + // prevent list (client-side) overflow + if(social->m_playerSocialMap.size() >= 255) + break; + } + while( result->NextRow() ); + delete result; + return social; +} diff --git a/src/game/SocialMgr.h b/src/game/SocialMgr.h new file mode 100644 index 00000000000..332ddafa51d --- /dev/null +++ b/src/game/SocialMgr.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MANGOS_SOCIALMGR_H +#define __MANGOS_SOCIALMGR_H + +#include "Policies/Singleton.h" +#include "Database/DatabaseEnv.h" +#include "Common.h" + +class SocialMgr; +class PlayerSocial; +class Player; +class WorldPacket; + +enum FriendStatus +{ + FRIEND_STATUS_OFFLINE = 0, + FRIEND_STATUS_ONLINE = 1, + FRIEND_STATUS_AFK = 2, + FRIEND_STATUS_UNK3 = 3, + FRIEND_STATUS_DND = 4 +}; + +enum SocialFlag +{ + SOCIAL_FLAG_FRIEND = 0x01, + SOCIAL_FLAG_IGNORED = 0x02, + SOCIAL_FLAG_MUTED = 0x04 // guessed +}; + +struct FriendInfo +{ + FriendStatus Status; + uint32 Flags; + uint32 Area; + uint32 Level; + uint32 Class; + std::string Note; + + FriendInfo() + { + Status = FRIEND_STATUS_OFFLINE; + Flags = 0; + Area = 0; + Level = 0; + Class = 0; + Note = ""; + } + + FriendInfo(uint32 flags, std::string note) + { + Status = FRIEND_STATUS_OFFLINE; + Flags = flags; + Area = 0; + Level = 0; + Class = 0; + Note = note; + } +}; + +typedef std::map PlayerSocialMap; +typedef std::map SocialMap; + +/// Results of friend related commands +enum FriendsResult +{ + FRIEND_DB_ERROR = 0x00, + FRIEND_LIST_FULL = 0x01, + FRIEND_ONLINE = 0x02, + FRIEND_OFFLINE = 0x03, + FRIEND_NOT_FOUND = 0x04, + FRIEND_REMOVED = 0x05, + FRIEND_ADDED_ONLINE = 0x06, + FRIEND_ADDED_OFFLINE = 0x07, + FRIEND_ALREADY = 0x08, + FRIEND_SELF = 0x09, + FRIEND_ENEMY = 0x0A, + FRIEND_IGNORE_FULL = 0x0B, + FRIEND_IGNORE_SELF = 0x0C, + FRIEND_IGNORE_NOT_FOUND = 0x0D, + FRIEND_IGNORE_ALREADY = 0x0E, + FRIEND_IGNORE_ADDED = 0x0F, + FRIEND_IGNORE_REMOVED = 0x10, + FRIEND_IGNORE_AMBIGUOUS = 0x11, // That name is ambiguous, type more of the player's server name + FRIEND_MUTE_FULL = 0x12, + FRIEND_MUTE_SELF = 0x13, + FRIEND_MUTE_NOT_FOUND = 0x14, + FRIEND_MUTE_ALREADY = 0x15, + FRIEND_MUTE_ADDED = 0x16, + FRIEND_MUTE_REMOVED = 0x17, + FRIEND_MUTE_AMBIGUOUS = 0x18, // That name is ambiguous, type more of the player's server name + FRIEND_UNK7 = 0x19, // no message at client + FRIEND_UNKNOWN = 0x1A // Unknown friend response from server +}; + +class PlayerSocial +{ + friend class SocialMgr; + public: + PlayerSocial(); + ~PlayerSocial(); + // adding/removing + bool AddToSocialList(uint32 friend_guid, bool ignore); + void RemoveFromSocialList(uint32 friend_guid, bool ignore); + void SetFriendNote(uint32 friend_guid, std::string note); + // Packet send's + void SendSocialList(); + // Misc + bool HasFriend(uint32 friend_guid); + bool HasIgnore(uint32 ignore_guid); + uint32 GetPlayerGUID() { return m_playerGUID; } + void SetPlayerGUID(uint32 guid) { m_playerGUID = guid; } + private: + PlayerSocialMap m_playerSocialMap; + uint32 m_playerGUID; +}; + +class SocialMgr +{ + public: + SocialMgr(); + ~SocialMgr(); + // Misc + void RemovePlayerSocial(uint32 guid); + void GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &friendInfo); + // Packet management + void MakeFriendStatusPacket(FriendsResult result, uint32 friend_guid, WorldPacket *data); + void SendFriendStatus(Player *player, FriendsResult result, uint32 friend_guid, std::string name, bool broadcast); + void BroadcastToFriendListers(Player *player, WorldPacket *packet); + // Loading + PlayerSocial *LoadFromDB(QueryResult *result, uint32 guid); + private: + SocialMap m_socialMap; +}; + +#define sSocialMgr MaNGOS::Singleton::Instance() +#endif diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp new file mode 100644 index 00000000000..95e9ac1b745 --- /dev/null +++ b/src/game/Spell.cpp @@ -0,0 +1,5115 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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_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 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 searcher(result, u_check); + + TypeContainerVisitor, GridTypeMapContainer > grid_searcher(searcher); + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_searcher, *MapManager::Instance().GetMap(m_caster->GetMapId(), m_caster)); + + if(!result) + { + TypeContainerVisitor, 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_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<GetTypeId() == TYPEID_PLAYER) + { + Player *me = (Player*)m_caster; + for (std::list::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::iterator itr = tmpUnitMap.begin() ; itr != tmpUnitMap.end();) + { + if(!CheckTarget(*itr, i, false )) + { + itr = tmpUnitMap.erase(itr); + continue; + } + else + ++itr; + } + + for(std::list::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::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + { + if (targetGUID == ihit->targetGUID) // Found in list + { + ihit->effectMask |= 1<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::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit) + { + if (targetGUID == ihit->targetGUID) // Found in list + { + ihit->effectMask |= 1<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::iterator ihit= m_UniqueItemInfo.begin();ihit != m_UniqueItemInfo.end();++ihit) + { + if (pitem == ihit->item) // Found in list + { + ihit->effectMask |= 1<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; + + // 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_STUNDED)) + 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<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<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<item, NULL, effectNumber); +} + +bool Spell::IsAliveUnitPresentInTargetList() +{ + // Not need check return true + if (m_needAliveTargetMask == 0) + return true; + + uint8 needAliveTargetMask = m_needAliveTargetMask; + + for(std::list::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* 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 +{ + 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* 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 &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 tempUnitMap; + + { + MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(m_caster, m_caster, max_range); + MaNGOS::UnitListSearcher searcher(tempUnitMap, u_check); + + TypeContainerVisitor, WorldTypeMapContainer > world_unit_searcher(searcher); + TypeContainerVisitor, GridTypeMapContainer > grid_unit_searcher(searcher); + + CellLock 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::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::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 tempUnitMap; + + { + MaNGOS::AnyAoETargetUnitInObjectRangeCheck u_check(pUnitTarget, originalCaster, max_range); + MaNGOS::UnitListSearcher searcher(tempUnitMap, u_check); + + TypeContainerVisitor, WorldTypeMapContainer > world_unit_searcher(searcher); + TypeContainerVisitor, GridTypeMapContainer > grid_unit_searcher(searcher); + + CellLock 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::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 world_object_notifier(notifier); + TypeContainerVisitor grid_object_notifier(notifier); + + CellLock 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 world_object_notifier(notifier); + TypeContainerVisitor grid_object_notifier(notifier); + + CellLock 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 world_object_notifier(notifier); + TypeContainerVisitor grid_object_notifier(notifier); + + CellLock 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 world_object_notifier(notifier); + TypeContainerVisitor grid_object_notifier(notifier); + + CellLock 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 world_object_notifier(notifier); + TypeContainerVisitor grid_object_notifier(notifier); + + CellLock 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 world_object_notifier(notifier); + TypeContainerVisitor grid_object_notifier(notifier); + + CellLock 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 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 world_object_notifier(notifier); + TypeContainerVisitor grid_object_notifier(notifier); + + CellLock 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::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 world_notifier(notifier); + TypeContainerVisitor grid_notifier(notifier); + CellLock 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 world_notifier(notifier); + TypeContainerVisitor grid_notifier(notifier); + + CellLock 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::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::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::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::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + DoAllEffectOnTarget(&(*ihit)); + + for(std::list::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::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::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::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_STUNDED | 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::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::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::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + if( ihit->effectMask & (1<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::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::iterator ighit= m_UniqueGOTargetInfo.begin();ighit != m_UniqueGOTargetInfo.end();++ighit) + *data << uint64(ighit->targetGUID); // Always hits + + *data << (uint8)m_countOfMiss; + for(std::list::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::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::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(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 ", 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 checker(p_GameObject,go_check); + + TypeContainerVisitor, GridTypeMapContainer > object_checker(checker); + CellLock 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 searcher(p_Creature, u_check); + + TypeContainerVisitor, GridTypeMapContainer > grid_creature_searcher(searcher); + + CellLock 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: + { + // Execute + if(m_spellInfo->SpellIconID == 1648) + { + 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) + { + if(m_caster->IsInWater()) + return SPELL_FAILED_ONLY_ABOVEWATER; + } + 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; + + // 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::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_IsTriggeredSpell && m_caster->GetTypeId() == TYPEID_PLAYER && + !IsPositiveSpell(m_spellInfo->Id) && !m_caster->HasInArc( M_PI, target ) ) + { + // Spell-Family Related Checks + switch (m_spellInfo->SpellFamilyName) + { + case SPELLFAMILY_PRIEST: + { + // Shadow Word: Death, castable without facing + if (m_spellInfo->SpellFamilyFlags & 0x200000000LL) + return 0; // this is not TARGET_FLAG_DEST_LOCATION so we can return safely + break; + } + case SPELLFAMILY_PALADIN: + { + // Holy Shock, require facing + if (m_spellInfo->SpellFamilyFlags & 0x200000LL) + return SPELL_FAILED_UNIT_NOT_INFRONT; + break; + } + case SPELLFAMILY_WARRIOR: + { + // Charge, require facing + if (m_spellInfo->SpellFamilyFlags & 1) + return SPELL_FAILED_UNIT_NOT_INFRONT; + break; + } + } + + // Ranged Weapon + if (IsRangedSpell()) + return SPELL_FAILED_UNIT_NOT_INFRONT; + + // Melee Combat + if (m_spellInfo->rangeIndex == 2) + return SPELL_FAILED_UNIT_NOT_INFRONT; + + // Missile Effect + if (m_spellInfo->speed > 0) + return SPELL_FAILED_UNIT_NOT_INFRONT; + + // Channeled Spells need facing + if (IsChanneledSpell(m_spellInfo)) + return SPELL_FAILED_UNIT_NOT_INFRONT; + + // Direct Damage and charge effects + for (uint8 i=0;i<3;++i) + { + if (m_spellInfo->Effect[i] == SPELL_EFFECT_SCHOOL_DAMAGE || + m_spellInfo->Effect[i] == SPELL_EFFECT_POWER_BURN || + m_spellInfo->Effect[i] == SPELL_EFFECT_HEALTH_LEECH || + m_spellInfo->Effect[i] == SPELL_EFFECT_CHARGE) + 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 checker(ok,go_check); + + TypeContainerVisitor, GridTypeMapContainer > object_checker(checker); + CellLock 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::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<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 ) + { + 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(magnet->IsWithinLOSInMap(m_caster)) + { + 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::const_iterator itr= m_UniqueTargetInfo.begin();itr != m_UniqueTargetInfo.end();++itr) + if(itr->effectMask & (1<::const_iterator itr= m_UniqueGOTargetInfo.begin();itr != m_UniqueGOTargetInfo.end();++itr) + if(itr->effectMask & (1<::const_iterator itr= m_UniqueItemInfo.begin();itr != m_UniqueItemInfo.end();++itr) + if(itr->effectMask & (1<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 new file mode 100644 index 00000000000..898d74de0f2 --- /dev/null +++ b/src/game/Spell.h @@ -0,0 +1,694 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __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 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 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 &TagUnitMap); + + Unit* SelectMagnetTarget(); + 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 m_UniqueTargetInfo; + uint8 m_needAliveTargetMask; // Mask req. alive targets + + struct GOTargetInfo + { + uint64 targetGUID; + uint64 timeDelay; + uint8 effectMask:8; + bool processed:1; + }; + std::list m_UniqueGOTargetInfo; + + struct ItemTargetInfo + { + Item *item; + uint8 effectMask; + }; + std::list 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 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 &i_data; + Spell &i_spell; + const uint32& i_index; + float i_radius; + Unit* i_originalCaster; + + SpellNotifierPlayer(Spell &spell, std::list &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 void Visit(GridRefManager &) {} + }; + + struct MANGOS_DLL_DECL SpellNotifierCreatureAndPlayer + { + std::list *i_data; + Spell &i_spell; + const uint32& i_push_type; + float i_radius; + SpellTargets i_TargetType; + Unit* i_originalCaster; + + SpellNotifierCreatureAndPlayer(Spell &spell, std::list &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 inline void Visit(GridRefManager &m) + { + assert(i_data); + + if(!i_originalCaster) + return; + + for(typename GridRefManager::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/SpellAuraDefines.h b/src/game/SpellAuraDefines.h new file mode 100644 index 00000000000..e9dbbb12adf --- /dev/null +++ b/src/game/SpellAuraDefines.h @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef MANGOS_SPELLAURADEFINES_H +#define MANGOS_SPELLAURADEFINES_H + +#define MAX_AURAS 56 +#define MAX_POSITIVE_AURAS 40 + +enum AURA_FLAGS +{ + AFLAG_NEGATIVE = 0x09, + AFLAG_POSITIVE = 0x1F, + AFLAG_MASK = 0xFF +}; + +//m_schoolAbsorb +enum DAMAGE_ABSORB_TYPE +{ + ALL_DAMAGE_ABSORB = -2, + ONLY_MAGIC_ABSORB = -1, +}; + +enum AuraType +{ + SPELL_AURA_NONE = 0, + SPELL_AURA_BIND_SIGHT = 1, + SPELL_AURA_MOD_POSSESS = 2, + SPELL_AURA_PERIODIC_DAMAGE = 3, + SPELL_AURA_DUMMY = 4, + SPELL_AURA_MOD_CONFUSE = 5, + SPELL_AURA_MOD_CHARM = 6, + SPELL_AURA_MOD_FEAR = 7, + SPELL_AURA_PERIODIC_HEAL = 8, + SPELL_AURA_MOD_ATTACKSPEED = 9, + SPELL_AURA_MOD_THREAT = 10, + SPELL_AURA_MOD_TAUNT = 11, + SPELL_AURA_MOD_STUN = 12, + SPELL_AURA_MOD_DAMAGE_DONE = 13, + SPELL_AURA_MOD_DAMAGE_TAKEN = 14, + SPELL_AURA_DAMAGE_SHIELD = 15, + SPELL_AURA_MOD_STEALTH = 16, + SPELL_AURA_MOD_DETECT = 17, + SPELL_AURA_MOD_INVISIBILITY = 18, + SPELL_AURA_MOD_INVISIBILITY_DETECTION = 19, + SPELL_AURA_OBS_MOD_HEALTH = 20, //20,21 unofficial + SPELL_AURA_OBS_MOD_MANA = 21, + SPELL_AURA_MOD_RESISTANCE = 22, + SPELL_AURA_PERIODIC_TRIGGER_SPELL = 23, + SPELL_AURA_PERIODIC_ENERGIZE = 24, + SPELL_AURA_MOD_PACIFY = 25, + SPELL_AURA_MOD_ROOT = 26, + SPELL_AURA_MOD_SILENCE = 27, + SPELL_AURA_REFLECT_SPELLS = 28, + SPELL_AURA_MOD_STAT = 29, + SPELL_AURA_MOD_SKILL = 30, + SPELL_AURA_MOD_INCREASE_SPEED = 31, + SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED = 32, + SPELL_AURA_MOD_DECREASE_SPEED = 33, + SPELL_AURA_MOD_INCREASE_HEALTH = 34, + SPELL_AURA_MOD_INCREASE_ENERGY = 35, + SPELL_AURA_MOD_SHAPESHIFT = 36, + SPELL_AURA_EFFECT_IMMUNITY = 37, + SPELL_AURA_STATE_IMMUNITY = 38, + SPELL_AURA_SCHOOL_IMMUNITY = 39, + SPELL_AURA_DAMAGE_IMMUNITY = 40, + SPELL_AURA_DISPEL_IMMUNITY = 41, + SPELL_AURA_PROC_TRIGGER_SPELL = 42, + SPELL_AURA_PROC_TRIGGER_DAMAGE = 43, + SPELL_AURA_TRACK_CREATURES = 44, + SPELL_AURA_TRACK_RESOURCES = 45, + SPELL_AURA_MOD_PARRY_SKILL = 46, + SPELL_AURA_MOD_PARRY_PERCENT = 47, + SPELL_AURA_MOD_DODGE_SKILL = 48, + SPELL_AURA_MOD_DODGE_PERCENT = 49, + SPELL_AURA_MOD_BLOCK_SKILL = 50, + SPELL_AURA_MOD_BLOCK_PERCENT = 51, + SPELL_AURA_MOD_CRIT_PERCENT = 52, + SPELL_AURA_PERIODIC_LEECH = 53, + SPELL_AURA_MOD_HIT_CHANCE = 54, + SPELL_AURA_MOD_SPELL_HIT_CHANCE = 55, + SPELL_AURA_TRANSFORM = 56, + SPELL_AURA_MOD_SPELL_CRIT_CHANCE = 57, + SPELL_AURA_MOD_INCREASE_SWIM_SPEED = 58, + SPELL_AURA_MOD_DAMAGE_DONE_CREATURE = 59, + SPELL_AURA_MOD_PACIFY_SILENCE = 60, + SPELL_AURA_MOD_SCALE = 61, + SPELL_AURA_PERIODIC_HEALTH_FUNNEL = 62, + SPELL_AURA_PERIODIC_MANA_FUNNEL = 63, + SPELL_AURA_PERIODIC_MANA_LEECH = 64, + SPELL_AURA_MOD_CASTING_SPEED = 65, + SPELL_AURA_FEIGN_DEATH = 66, + SPELL_AURA_MOD_DISARM = 67, + SPELL_AURA_MOD_STALKED = 68, + SPELL_AURA_SCHOOL_ABSORB = 69, + SPELL_AURA_EXTRA_ATTACKS = 70, + SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL = 71, + SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT = 72, + SPELL_AURA_MOD_POWER_COST_SCHOOL = 73, + SPELL_AURA_REFLECT_SPELLS_SCHOOL = 74, + SPELL_AURA_MOD_LANGUAGE = 75, + SPELL_AURA_FAR_SIGHT = 76, + SPELL_AURA_MECHANIC_IMMUNITY = 77, + SPELL_AURA_MOUNTED = 78, + SPELL_AURA_MOD_DAMAGE_PERCENT_DONE = 79, + SPELL_AURA_MOD_PERCENT_STAT = 80, + SPELL_AURA_SPLIT_DAMAGE_PCT = 81, + SPELL_AURA_WATER_BREATHING = 82, + SPELL_AURA_MOD_BASE_RESISTANCE = 83, + SPELL_AURA_MOD_REGEN = 84, + SPELL_AURA_MOD_POWER_REGEN = 85, + SPELL_AURA_CHANNEL_DEATH_ITEM = 86, + SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN = 87, + SPELL_AURA_MOD_HEALTH_REGEN_PERCENT = 88, + SPELL_AURA_PERIODIC_DAMAGE_PERCENT = 89, + SPELL_AURA_MOD_RESIST_CHANCE = 90, + SPELL_AURA_MOD_DETECT_RANGE = 91, + SPELL_AURA_PREVENTS_FLEEING = 92, + SPELL_AURA_MOD_UNATTACKABLE = 93, + SPELL_AURA_INTERRUPT_REGEN = 94, + SPELL_AURA_GHOST = 95, + SPELL_AURA_SPELL_MAGNET = 96, + SPELL_AURA_MANA_SHIELD = 97, + SPELL_AURA_MOD_SKILL_TALENT = 98, + SPELL_AURA_MOD_ATTACK_POWER = 99, + SPELL_AURA_AURAS_VISIBLE = 100, + SPELL_AURA_MOD_RESISTANCE_PCT = 101, + SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS = 102, + SPELL_AURA_MOD_TOTAL_THREAT = 103, + SPELL_AURA_WATER_WALK = 104, + SPELL_AURA_FEATHER_FALL = 105, + SPELL_AURA_HOVER = 106, + SPELL_AURA_ADD_FLAT_MODIFIER = 107, + SPELL_AURA_ADD_PCT_MODIFIER = 108, + SPELL_AURA_ADD_TARGET_TRIGGER = 109, + SPELL_AURA_MOD_POWER_REGEN_PERCENT = 110, + SPELL_AURA_ADD_CASTER_HIT_TRIGGER = 111, + SPELL_AURA_OVERRIDE_CLASS_SCRIPTS = 112, + SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN = 113, + SPELL_AURA_MOD_RANGED_DAMAGE_TAKEN_PCT = 114, + SPELL_AURA_MOD_HEALING = 115, + SPELL_AURA_MOD_REGEN_DURING_COMBAT = 116, + SPELL_AURA_MOD_MECHANIC_RESISTANCE = 117, + SPELL_AURA_MOD_HEALING_PCT = 118, + SPELL_AURA_SHARE_PET_TRACKING = 119, + SPELL_AURA_UNTRACKABLE = 120, + SPELL_AURA_EMPATHY = 121, + SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT = 122, + SPELL_AURA_MOD_TARGET_RESISTANCE = 123, + SPELL_AURA_MOD_RANGED_ATTACK_POWER = 124, + SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN = 125, + SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT = 126, + SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS = 127, + SPELL_AURA_MOD_POSSESS_PET = 128, + SPELL_AURA_MOD_SPEED_ALWAYS = 129, + SPELL_AURA_MOD_MOUNTED_SPEED_ALWAYS = 130, + SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS = 131, + SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT = 132, + SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT = 133, + SPELL_AURA_MOD_MANA_REGEN_INTERRUPT = 134, + SPELL_AURA_MOD_HEALING_DONE = 135, + SPELL_AURA_MOD_HEALING_DONE_PERCENT = 136, + SPELL_AURA_MOD_TOTAL_STAT_PERCENTAGE = 137, + SPELL_AURA_MOD_HASTE = 138, + SPELL_AURA_FORCE_REACTION = 139, + SPELL_AURA_MOD_RANGED_HASTE = 140, + SPELL_AURA_MOD_RANGED_AMMO_HASTE = 141, + SPELL_AURA_MOD_BASE_RESISTANCE_PCT = 142, + SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE = 143, + SPELL_AURA_SAFE_FALL = 144, + SPELL_AURA_CHARISMA = 145, + SPELL_AURA_PERSUADED = 146, + SPELL_AURA_ADD_CREATURE_IMMUNITY = 147, + SPELL_AURA_RETAIN_COMBO_POINTS = 148, + SPELL_AURA_RESIST_PUSHBACK = 149, // Resist Pushback + SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT = 150, + SPELL_AURA_TRACK_STEALTHED = 151, // Track Stealthed + SPELL_AURA_MOD_DETECTED_RANGE = 152, // Mod Detected Range + SPELL_AURA_SPLIT_DAMAGE_FLAT = 153, // Split Damage Flat + SPELL_AURA_MOD_STEALTH_LEVEL = 154, // Stealth Level Modifier + SPELL_AURA_MOD_WATER_BREATHING = 155, // Mod Water Breathing + SPELL_AURA_MOD_REPUTATION_GAIN = 156, // Mod Reputation Gain + SPELL_AURA_PET_DAMAGE_MULTI = 157, // Mod Pet Damage + SPELL_AURA_MOD_SHIELD_BLOCKVALUE = 158, + SPELL_AURA_NO_PVP_CREDIT = 159, + SPELL_AURA_MOD_AOE_AVOIDANCE = 160, + SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT = 161, + SPELL_AURA_POWER_BURN_MANA = 162, + SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE = 163, + SPELL_AURA_164 = 164, + SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS = 165, + SPELL_AURA_MOD_ATTACK_POWER_PCT = 166, + SPELL_AURA_MOD_RANGED_ATTACK_POWER_PCT = 167, + SPELL_AURA_MOD_DAMAGE_DONE_VERSUS = 168, + SPELL_AURA_MOD_CRIT_PERCENT_VERSUS = 169, + SPELL_AURA_DETECT_AMORE = 170, + SPELL_AURA_MOD_SPEED_NOT_STACK = 171, + SPELL_AURA_MOD_MOUNTED_SPEED_NOT_STACK = 172, + SPELL_AURA_ALLOW_CHAMPION_SPELLS = 173, + SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT = 174, // by defeult intelect, dependent from SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT + SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT = 175, + SPELL_AURA_SPIRIT_OF_REDEMPTION = 176, + SPELL_AURA_AOE_CHARM = 177, + SPELL_AURA_MOD_DEBUFF_RESISTANCE = 178, + SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE = 179, + SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS = 180, + SPELL_AURA_MOD_FLAT_SPELL_CRIT_DAMAGE_VERSUS = 181, // unused - possible flat spell crit damage versus + SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT = 182, + SPELL_AURA_MOD_CRITICAL_THREAT = 183, + SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE = 184, + SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE= 185, + SPELL_AURA_MOD_ATTACKER_SPELL_HIT_CHANCE = 186, + SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE = 187, + SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE = 188, + SPELL_AURA_MOD_RATING = 189, + SPELL_AURA_MOD_FACTION_REPUTATION_GAIN = 190, + SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED = 191, + SPELL_AURA_HASTE_MELEE = 192, + SPELL_AURA_MELEE_SLOW = 193, + SPELL_AURA_MOD_DEPRICATED_1 = 194, // not used now, old SPELL_AURA_MOD_SPELL_DAMAGE_OF_INTELLECT + SPELL_AURA_MOD_DEPRICATED_2 = 195, // not used now, old SPELL_AURA_MOD_SPELL_HEALING_OF_INTELLECT + SPELL_AURA_MOD_COOLDOWN = 196, // only 24818 Noxious Breath + SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE = 197, + SPELL_AURA_MOD_ALL_WEAPON_SKILLS = 198, + SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT = 199, + SPELL_AURA_MOD_XP_PCT = 200, + SPELL_AURA_FLY = 201, + SPELL_AURA_IGNORE_COMBAT_RESULT = 202, + SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE = 203, + SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE = 204, + SPELL_AURA_205 = 205, // unused + SPELL_AURA_MOD_SPEED_MOUNTED = 206, // ? used in strange spells + SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED = 207, + SPELL_AURA_MOD_SPEED_FLIGHT = 208, + SPELL_AURA_MOD_FLIGHT_SPEED_ALWAYS = 209, + SPELL_AURA_210 = 210, // unused + SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK = 211, + SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT = 212, + SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT = 213, + SPELL_AURA_214 = 214, + SPELL_AURA_ARENA_PREPARATION = 215, + SPELL_AURA_HASTE_SPELLS = 216, + SPELL_AURA_217 = 217, + SPELL_AURA_HASTE_RANGED = 218, + SPELL_AURA_MOD_MANA_REGEN_FROM_STAT = 219, + SPELL_AURA_MOD_RATING_FROM_STAT = 220, + SPELL_AURA_221 = 221, + SPELL_AURA_222 = 222, + SPELL_AURA_223 = 223, + SPELL_AURA_224 = 224, + SPELL_AURA_PRAYER_OF_MENDING = 225, + SPELL_AURA_PERIODIC_DUMMY = 226, + SPELL_AURA_227 = 227, + SPELL_AURA_DETECT_STEALTH = 228, + SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE = 229, + SPELL_AURA_230 = 230, + SPELL_AURA_231 = 231, + SPELL_AURA_MECHANIC_DURATION_MOD = 232, + SPELL_AURA_233 = 233, + SPELL_AURA_MECHANIC_DURATION_MOD_NOT_STACK = 234, + SPELL_AURA_MOD_DISPEL_RESIST = 235, + SPELL_AURA_236 = 236, + SPELL_AURA_MOD_SPELL_DAMAGE_OF_ATTACK_POWER = 237, + SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER = 238, + SPELL_AURA_MOD_SCALE_2 = 239, + SPELL_AURA_MOD_EXPERTISE = 240, + SPELL_AURA_FORCE_MOVE_FORWARD = 241, + SPELL_AURA_MOD_SPELL_DAMAGE_FROM_HEALING = 242, + SPELL_AURA_243 = 243, + SPELL_AURA_COMPREHEND_LANGUAGE = 244, + SPELL_AURA_MOD_DURATION_OF_MAGIC_EFFECTS = 245, + SPELL_AURA_246 = 246, + SPELL_AURA_247 = 247, + SPELL_AURA_MOD_COMBAT_RESULT_CHANCE = 248, + SPELL_AURA_249 = 249, + SPELL_AURA_MOD_INCREASE_HEALTH_2 = 250, + SPELL_AURA_MOD_ENEMY_DODGE = 251, + SPELL_AURA_252 = 252, + SPELL_AURA_253 = 253, + SPELL_AURA_254 = 254, + SPELL_AURA_255 = 255, + SPELL_AURA_256 = 256, + SPELL_AURA_257 = 257, + SPELL_AURA_258 = 258, + SPELL_AURA_259 = 259, + SPELL_AURA_260 = 260, + SPELL_AURA_261 = 261, + TOTAL_AURAS=262 +}; + +enum AreaAuraType +{ + AREA_AURA_PARTY, + AREA_AURA_FRIEND, + AREA_AURA_ENEMY, + AREA_AURA_PET, + AREA_AURA_OWNER +}; +#endif diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp new file mode 100644 index 00000000000..e5abe4d842f --- /dev/null +++ b/src/game/SpellAuras.cpp @@ -0,0 +1,6360 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 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 searcher(targets, u_check); + TypeContainerVisitor, WorldTypeMapContainer > world_unit_searcher(searcher); + TypeContainerVisitor, GridTypeMapContainer > grid_unit_searcher(searcher); + CellLock 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 searcher(targets, u_check); + TypeContainerVisitor, WorldTypeMapContainer > world_unit_searcher(searcher); + TypeContainerVisitor, GridTypeMapContainer > grid_unit_searcher(searcher); + CellLock 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::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= 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;iitems.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<GetGUID(); + data<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<GetGUID(); + data<SendMessageToSet(&data,true); + */ + // blizz like 2.0.x + m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNKNOWN6); + // blizz like 2.0.x + m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH); + // blizz like 2.0.x + m_target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD); + + m_target->clearUnitState(UNIT_STAT_DIED); + } +} + +void Aura::HandleAuraModDisarm(bool apply, bool Real) +{ + if(!Real) + return; + + if(!apply && m_target->HasAuraType(SPELL_AURA_MOD_DISARM)) + return; + + // not sure for it's correctness + if(apply) + m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED); + else + m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED); + + // only at real add/remove aura + if (m_target->GetTypeId() != TYPEID_PLAYER) + return; + + // main-hand attack speed already set to special value for feral form already and don't must chnage and reset at remove. + if (((Player *)m_target)->IsInFeralForm()) + return; + + if (apply) + m_target->SetAttackTime(BASE_ATTACK,BASE_ATTACK_TIME); + else + ((Player *)m_target)->SetRegularAttackTime(); + + m_target->UpdateDamagePhysical(BASE_ATTACK); +} + +void Aura::HandleAuraModStun(bool apply, bool Real) +{ + if(!Real) + return; + + if (apply) + { + m_target->addUnitState(UNIT_STAT_STUNDED); + m_target->SetUInt64Value(UNIT_FIELD_TARGET, 0); + + m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + m_target->CastStop(m_target->GetGUID() == GetCasterGUID() ? GetId() : 0); + + // Creature specific + if(m_target->GetTypeId() != TYPEID_PLAYER) + ((Creature*)m_target)->StopMoving(); + else + m_target->SetUnitMovementFlags(0); //Clear movement flags + + WorldPacket data(SMSG_FORCE_MOVE_ROOT, 8); + + data.append(m_target->GetPackGUID()); + data << uint32(0); + m_target->SendMessageToSet(&data,true); + } + else + { + // Real remove called after current aura remove from lists, check if other similar auras active + if(m_target->HasAuraType(SPELL_AURA_MOD_STUN)) + return; + + m_target->clearUnitState(UNIT_STAT_STUNDED); + m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); + + if(!m_target->hasUnitState(UNIT_STAT_ROOT)) // prevent allow move if have also root effect + { + if(m_target->getVictim() && m_target->isAlive()) + m_target->SetUInt64Value(UNIT_FIELD_TARGET,m_target->getVictim()->GetGUID() ); + + WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 8+4); + data.append(m_target->GetPackGUID()); + data << uint32(0); + m_target->SendMessageToSet(&data,true); + } + + // Wyvern Sting + if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_HUNTER && GetSpellProto()->SpellIconID == 1721) + { + Unit* caster = GetCaster(); + if( !caster || caster->GetTypeId()!=TYPEID_PLAYER ) + return; + + uint32 spell_id = 0; + + switch(GetId()) + { + case 19386: spell_id = 24131; break; + case 24132: spell_id = 24134; break; + case 24133: spell_id = 24135; break; + case 27068: spell_id = 27069; break; + default: + sLog.outError("Spell selection called for unexpected original spell %u, new spell for this spell family?",GetId()); + return; + } + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell_id); + + if(!spellInfo) + return; + + caster->CastSpell(m_target,spellInfo,true,NULL,this); + return; + } + } +} + +void Aura::HandleModStealth(bool apply, bool Real) +{ + if(apply) + { + // drop flag at stealth in bg + if(Real && m_target->GetTypeId()==TYPEID_PLAYER && ((Player*)m_target)->InBattleGround()) + if(BattleGround *bg = ((Player*)m_target)->GetBattleGround()) + bg->EventPlayerDroppedFlag((Player*)m_target); + + // only at real aura add + if(Real) + { + m_target->SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x02); + if(m_target->GetTypeId()==TYPEID_PLAYER) + m_target->SetFlag(PLAYER_FIELD_BYTES2, 0x2000); + + // apply only if not in GM invisibility (and overwrite invisibility state) + if(m_target->GetVisibility()!=VISIBILITY_OFF) + { + m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT); + m_target->SetVisibility(VISIBILITY_GROUP_STEALTH); + } + + // for RACE_NIGHTELF stealth + if(m_target->GetTypeId()==TYPEID_PLAYER && GetId()==20580) + m_target->CastSpell(m_target, 21009, true, NULL, this); + } + } + else + { + // only at real aura remove + if(Real) + { + // for RACE_NIGHTELF stealth + if(m_target->GetTypeId()==TYPEID_PLAYER && GetId()==20580) + m_target->RemoveAurasDueToSpell(21009); + + // if last SPELL_AURA_MOD_STEALTH and no GM invisibility + if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH) && m_target->GetVisibility()!=VISIBILITY_OFF) + { + m_target->SetByteValue(UNIT_FIELD_BYTES_1, 2, 0x00); + if(m_target->GetTypeId()==TYPEID_PLAYER) + m_target->RemoveFlag(PLAYER_FIELD_BYTES2, 0x2000); + + // restore invisibility if any + if(m_target->HasAuraType(SPELL_AURA_MOD_INVISIBILITY)) + { + m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT); + m_target->SetVisibility(VISIBILITY_GROUP_INVISIBILITY); + } + else + m_target->SetVisibility(VISIBILITY_ON); + } + } + } + + // Master of Subtlety + Unit::AuraList const& mDummyAuras = m_target->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator i = mDummyAuras.begin();i != mDummyAuras.end(); ++i) + { + if ((*i)->GetSpellProto()->SpellIconID == 2114) + { + if (apply) + { + int32 bp = (*i)->GetModifier()->m_amount; + m_target->CastCustomSpell(m_target,31665,&bp,NULL,NULL,true); + } + else + m_target->CastSpell(m_target,31666,true); + break; + } + } +} + +void Aura::HandleInvisibility(bool apply, bool Real) +{ + if(apply) + { + m_target->m_invisibilityMask |= (1 << m_modifier.m_miscvalue); + + if(Real && m_target->GetTypeId()==TYPEID_PLAYER) + { + // apply glow vision + m_target->SetFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); + + // drop flag at invisible in bg + if(((Player*)m_target)->InBattleGround()) + if(BattleGround *bg = ((Player*)m_target)->GetBattleGround()) + bg->EventPlayerDroppedFlag((Player*)m_target); + } + + // apply only if not in GM invisibility and not stealth + if(m_target->GetVisibility()==VISIBILITY_ON) + { + // Aura not added yet but visibility code expect temporary add aura + m_target->SetVisibility(VISIBILITY_GROUP_NO_DETECT); + m_target->SetVisibility(VISIBILITY_GROUP_INVISIBILITY); + } + } + else + { + // recalculate value at modifier remove (current aura already removed) + m_target->m_invisibilityMask = 0; + Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY); + for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + m_target->m_invisibilityMask |= (1 << m_modifier.m_miscvalue); + + // only at real aura remove and if not have different invisibility auras. + if(Real && m_target->m_invisibilityMask==0) + { + // remove glow vision + if(m_target->GetTypeId() == TYPEID_PLAYER) + m_target->RemoveFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW); + + // apply only if not in GM invisibility & not stealthed while invisible + if(m_target->GetVisibility()!=VISIBILITY_OFF) + { + // if have stealth aura then already have stealth visibility + if(!m_target->HasAuraType(SPELL_AURA_MOD_STEALTH)) + m_target->SetVisibility(VISIBILITY_ON); + } + } + } +} + +void Aura::HandleInvisibilityDetect(bool apply, bool Real) +{ + if(apply) + { + m_target->m_detectInvisibilityMask |= (1 << m_modifier.m_miscvalue); + } + else + { + // recalculate value at modifier remove (current aura already removed) + m_target->m_detectInvisibilityMask = 0; + Unit::AuraList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY_DETECTION); + for(Unit::AuraList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + m_target->m_detectInvisibilityMask |= (1 << m_modifier.m_miscvalue); + } + if(Real && m_target->GetTypeId()==TYPEID_PLAYER) + ObjectAccessor::UpdateVisibilityForPlayer((Player*)m_target); +} + +void Aura::HandleAuraModRoot(bool apply, bool Real) +{ + // only at real add/remove aura + if(!Real) + return; + + uint32 apply_stat = UNIT_STAT_ROOT; + if (apply) + { + m_target->addUnitState(UNIT_STAT_ROOT); + m_target->SetUInt64Value (UNIT_FIELD_TARGET, 0); + // probably wrong + m_target->SetFlag(UNIT_FIELD_FLAGS,(apply_stat<<16)); + + //Save last orientation + if( m_target->getVictim() ) + m_target->SetOrientation(m_target->GetAngle(m_target->getVictim())); + + if(m_target->GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10); + data.append(m_target->GetPackGUID()); + data << (uint32)2; + m_target->SendMessageToSet(&data,true); + + //Clear unit movement flags + m_target->SetUnitMovementFlags(0); + } + else + ((Creature *)m_target)->StopMoving(); + } + else + { + // Real remove called after current aura remove from lists, check if other similar auras active + if(m_target->HasAuraType(SPELL_AURA_MOD_ROOT)) + return; + + m_target->clearUnitState(UNIT_STAT_ROOT); + // probably wrong + m_target->RemoveFlag(UNIT_FIELD_FLAGS,(apply_stat<<16)); + + if(!m_target->hasUnitState(UNIT_STAT_STUNDED)) // prevent allow move if have also stun effect + { + if(m_target->getVictim() && m_target->isAlive()) + m_target->SetUInt64Value (UNIT_FIELD_TARGET,m_target->getVictim()->GetGUID() ); + + if(m_target->GetTypeId() == TYPEID_PLAYER) + { + WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 10); + data.append(m_target->GetPackGUID()); + data << (uint32)2; + m_target->SendMessageToSet(&data,true); + } + } + } +} + +void Aura::HandleAuraModSilence(bool apply, bool Real) +{ + // only at real add/remove aura + if(!Real) + return; + + if(apply) + { + m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED); + // Stop cast only spells vs PreventionType == SPELL_PREVENTION_TYPE_SILENCE + for (uint32 i = CURRENT_MELEE_SPELL; i < CURRENT_MAX_SPELL;i++) + { + Spell* currentSpell = m_target->m_currentSpells[i]; + if (currentSpell && currentSpell->m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) + { + uint32 state = currentSpell->getState(); + // Stop spells on prepere or casting state + if ( state == SPELL_STATE_PREPARING || state == SPELL_STATE_CASTING ) + { + currentSpell->cancel(); + currentSpell->SetDeletable(true); + m_target->m_currentSpells[i] = NULL; + } + } + } + + switch (GetId()) + { + // Arcane Torrent (Energy) + case 25046: + { + Unit * caster = GetCaster(); + if (!caster) + return; + + // Search Mana Tap auras on caster + int32 energy = 0; + Unit::AuraList const& m_dummyAuras = caster->GetAurasByType(SPELL_AURA_DUMMY); + for(Unit::AuraList::const_iterator i = m_dummyAuras.begin(); i != m_dummyAuras.end(); ++i) + if ((*i)->GetId() == 28734) + ++energy; + if (energy) + { + energy *= 10; + caster->CastCustomSpell(caster, 25048, &energy, NULL, NULL, true); + caster->RemoveAurasDueToSpell(28734); + } + } + } + } + else + { + // Real remove called after current aura remove from lists, check if other similar auras active + if(m_target->HasAuraType(SPELL_AURA_MOD_SILENCE)) + return; + + m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED); + } +} + +void Aura::HandleModThreat(bool apply, bool Real) +{ + // only at real add/remove aura + if(!Real) + return; + + if(!m_target->isAlive()) + return; + + Unit* caster = GetCaster(); + + if(!caster || !caster->isAlive()) + return; + + int level_diff = 0; + int multiplier = 0; + switch (GetId()) + { + // Arcane Shroud + case 26400: + level_diff = m_target->getLevel() - 60; + multiplier = 2; + break; + // The Eye of Diminution + case 28862: + level_diff = m_target->getLevel() - 60; + multiplier = 1; + break; + } + if (level_diff > 0) + m_modifier.m_amount += multiplier * level_diff; + + for(int8 x=0;x < MAX_SPELL_SCHOOL;x++) + { + if(m_modifier.m_miscvalue & int32(1<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<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<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<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<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<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<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<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<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<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<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<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<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/SpellAuras.h b/src/game/SpellAuras.h new file mode 100644 index 00000000000..24b94202978 --- /dev/null +++ b/src/game/SpellAuras.h @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef MANGOS_SPELLAURAS_H +#define MANGOS_SPELLAURAS_H + +#include "SpellAuraDefines.h" + +struct DamageManaShield +{ + uint32 m_spellId; + uint32 m_modType; + int32 m_schoolType; + uint32 m_totalAbsorb; + uint32 m_currAbsorb; +}; + +struct Modifier +{ + AuraType m_auraname; + int32 m_amount; + int32 m_miscvalue; + uint32 periodictime; +}; + +class Unit; +struct SpellEntry; +struct SpellModifier; +struct ProcTriggerSpell; + +// forward decl +class Aura; + +typedef void(Aura::*pAuraHandler)(bool Apply, bool Real); +// Real == true at aura add/remove +// Real == false at aura mod unapply/reapply; when adding/removing dependent aura/item/stat mods +// +// Code in aura handler can be guarded by if(Real) check if it should execution only at real add/remove of aura +// +// MAIN RULE: Code MUST NOT be guarded by if(Real) check if it modifies any stats +// (percent auras, stats mods, etc) +// Second rule: Code must be guarded by if(Real) check if it modifies object state (start/stop attack, send packets to client, etc) +// +// Other case choice: each code line moved under if(Real) check is mangos speedup, +// each setting object update field code line moved under if(Real) check is significant mangos speedup, and less server->client data sends +// each packet sending code moved under if(Real) check is _large_ mangos speedup, and lot less server->client data sends + +class MANGOS_DLL_SPEC Aura +{ + friend Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem); + + public: + //aura handlers + void HandleNULL(bool, bool) + { + // NOT IMPLEMENTED + } + void HandleUnused(bool, bool) + { + // NOT USED BY ANY SPELL OR USELESS + } + void HandleNoImmediateEffect(bool, bool) + { + // aura not have immediate effect at add/remove and handled by ID in other code place + } + void HandleBindSight(bool Apply, bool Real); + void HandleModPossess(bool Apply, bool Real); + void HandlePeriodicDamage(bool Apply, bool Real); + void HandleAuraDummy(bool Apply, bool Real); + void HandleAuraPeriodicDummy(bool apply, bool Real); + void HandleModConfuse(bool Apply, bool Real); + void HandleModCharm(bool Apply, bool Real); + void HandleModFear(bool Apply, bool Real); + void HandlePeriodicHeal(bool Apply, bool Real); + void HandleModAttackSpeed(bool Apply, bool Real); + void HandleModMeleeRangedSpeedPct(bool apply, bool Real); + void HandleModCombatSpeedPct(bool apply, bool Real); + void HandleModThreat(bool Apply, bool Real); + void HandleModTaunt(bool Apply, bool Real); + void HandleFeignDeath(bool Apply, bool Real); + void HandleAuraModDisarm(bool Apply, bool Real); + void HandleAuraModStalked(bool Apply, bool Real); + void HandleAuraWaterWalk(bool Apply, bool Real); + void HandleAuraFeatherFall(bool Apply, bool Real); + void HandleAuraHover(bool Apply, bool Real); + void HandleAddModifier(bool Apply, bool Real); + void HandleAuraModStun(bool Apply, bool Real); + void HandleModDamageDone(bool Apply, bool Real); + void HandleAuraUntrackable(bool Apply, bool Real); + void HandleAuraEmpathy(bool Apply, bool Real); + void HandleModOffhandDamagePercent(bool apply, bool Real); + void HandleAuraModRangedAttackPower(bool Apply, bool Real); + void HandleAuraModIncreaseEnergyPercent(bool Apply, bool Real); + void HandleAuraModIncreaseHealthPercent(bool Apply, bool Real); + void HandleAuraModRegenInterrupt(bool Apply, bool Real); + void HandleHaste(bool Apply, bool Real); + void HandlePeriodicTriggerSpell(bool Apply, bool Real); + void HandlePeriodicEnergize(bool Apply, bool Real); + void HandleAuraModResistanceExclusive(bool Apply, bool Real); + void HandleModStealth(bool Apply, bool Real); + void HandleInvisibility(bool Apply, bool Real); + void HandleInvisibilityDetect(bool Apply, bool Real); + void HandleAuraModTotalHealthPercentRegen(bool Apply, bool Real); + void HandleAuraModTotalManaPercentRegen(bool Apply, bool Real); + void HandleAuraModResistance(bool Apply, bool Real); + void HandleAuraModRoot(bool Apply, bool Real); + void HandleAuraModSilence(bool Apply, bool Real); + void HandleAuraModStat(bool Apply, bool Real); + void HandleAuraModIncreaseSpeed(bool Apply, bool Real); + void HandleAuraModIncreaseMountedSpeed(bool Apply, bool Real); + void HandleAuraModIncreaseFlightSpeed(bool Apply, bool Real); + void HandleAuraModDecreaseSpeed(bool Apply, bool Real); + void HandleAuraModUseNormalSpeed(bool Apply, bool Real); + void HandleAuraModIncreaseHealth(bool Apply, bool Real); + void HandleAuraModIncreaseEnergy(bool Apply, bool Real); + void HandleAuraModShapeshift(bool Apply, bool Real); + void HandleAuraModEffectImmunity(bool Apply, bool Real); + void HandleAuraModStateImmunity(bool Apply, bool Real); + void HandleAuraModSchoolImmunity(bool Apply, bool Real); + void HandleAuraModDmgImmunity(bool Apply, bool Real); + void HandleAuraModDispelImmunity(bool Apply, bool Real); + void HandleAuraProcTriggerSpell(bool Apply, bool Real); + void HandleAuraTrackCreatures(bool Apply, bool Real); + void HandleAuraTrackResources(bool Apply, bool Real); + void HandleAuraModParryPercent(bool Apply, bool Real); + void HandleAuraModDodgePercent(bool Apply, bool Real); + void HandleAuraModBlockPercent(bool Apply, bool Real); + void HandleAuraModCritPercent(bool Apply, bool Real); + void HandlePeriodicLeech(bool Apply, bool Real); + void HandleModHitChance(bool Apply, bool Real); + void HandleModSpellHitChance(bool Apply, bool Real); + void HandleAuraModScale(bool Apply, bool Real); + void HandlePeriodicManaLeech(bool Apply, bool Real); + void HandleModCastingSpeed(bool Apply, bool Real); + void HandleAuraMounted(bool Apply, bool Real); + void HandleWaterBreathing(bool Apply, bool Real); + void HandleModBaseResistance(bool Apply, bool Real); + void HandleModRegen(bool Apply, bool Real); + void HandleModPowerRegen(bool Apply, bool Real); + void HandleModPowerRegenPCT(bool Apply, bool Real); + void HandleChannelDeathItem(bool Apply, bool Real); + void HandlePeriodicDamagePCT(bool Apply, bool Real); + void HandleAuraModAttackPower(bool Apply, bool Real); + void HandleAuraTransform(bool Apply, bool Real); + void HandleModSpellCritChance(bool Apply, bool Real); + void HandleAuraModIncreaseSwimSpeed(bool Apply, bool Real); + void HandleModPowerCostPCT(bool Apply, bool Real); + void HandleModPowerCost(bool Apply, bool Real); + void HandleFarSight(bool Apply, bool Real); + void HandleModPossessPet(bool Apply, bool Real); + void HandleModMechanicImmunity(bool Apply, bool Real); + void HandleAuraModSkill(bool Apply, bool Real); + void HandleModDamagePercentDone(bool Apply, bool Real); + void HandleModPercentStat(bool Apply, bool Real); + void HandleModResistancePercent(bool Apply, bool Real); + void HandleAuraModBaseResistancePCT(bool Apply, bool Real); + void HandleModShieldBlockPCT(bool Apply, bool Real); + void HandleAuraTrackStealthed(bool Apply, bool Real); + void HandleModShieldBlock(bool Apply, bool Real); + void HandleForceReaction(bool Apply, bool Real); + void HandleAuraModRangedHaste(bool Apply, bool Real); + void HandleRangedAmmoHaste(bool Apply, bool Real); + void HandleModHealingDone(bool Apply, bool Real); + void HandleModTotalPercentStat(bool Apply, bool Real); + void HandleAuraModTotalThreat(bool Apply, bool Real); + void HandleModUnattackable(bool Apply, bool Real); + void HandleAuraModPacify(bool Apply, bool Real); + void HandleAuraGhost(bool Apply, bool Real); + void HandleAuraAllowFlight(bool Apply, bool Real); + void HandleModRating(bool apply, bool Real); + void HandleModTargetResistance(bool apply, bool Real); + void HandleAuraAttackPowerAttacker(bool apply, bool Real); + void HandleAuraModAttackPowerPercent(bool apply, bool Real); + void HandleAuraModRangedAttackPowerPercent(bool apply, bool Real); + void HandleAuraModRangedAttackPowerOfStatPercent(bool apply, bool Real); + void HandleSpiritOfRedemption(bool apply, bool Real); + void HandleAuraHealingPct(bool apply, bool Real); + void HandleModManaRegen(bool apply, bool Real); + void HandleComprehendLanguage(bool apply, bool Real); + void HandleAuraHealing(bool apply, bool Real); + void HandleShieldBlockValue(bool apply, bool Real); + void HandleModSpellCritChanceShool(bool apply, bool Real); + void HandleAuraRetainComboPoints(bool apply, bool Real); + void HandleModSpellDamagePercentFromStat(bool apply, bool Real); + void HandleModSpellHealingPercentFromStat(bool apply, bool Real); + void HandleAuraModDispelResist(bool apply, bool Real); + void HandleModSpellDamagePercentFromAttackPower(bool apply, bool Real); + void HandleModSpellHealingPercentFromAttackPower(bool apply, bool Real); + void HandleAuraModPacifyAndSilence(bool Apply, bool Real); + void HandleAuraModIncreaseMaxHealth(bool apply, bool Real); + void HandleAuraModExpertise(bool apply, bool Real); + void HandleForceMoveForward(bool apply, bool Real); + void HandleAuraModResistenceOfStatPercent(bool apply, bool Real); + void HandleAuraPowerBurn(bool apply, bool Real); + void HandleSchoolAbsorb(bool apply, bool Real); + void HandlePreventFleeing(bool apply, bool Real); + void HandleManaShield(bool apply, bool Real); + void HandleArenaPreparation(bool apply, bool Real); + + virtual ~Aura(); + + void SetModifier(AuraType t, int32 a, uint32 pt, int32 miscValue); + Modifier* GetModifier() {return &m_modifier;} + int32 GetMiscValue() {return m_spellProto->EffectMiscValue[m_effIndex];} + int32 GetMiscBValue() {return m_spellProto->EffectMiscValueB[m_effIndex];} + + SpellEntry const* GetSpellProto() const { return m_spellProto; } + uint32 GetId() const{ return m_spellProto->Id; } + uint64 GetCastItemGUID() const { return m_castItemGuid; } + uint32 GetEffIndex() const{ return m_effIndex; } + int32 GetBasePoints() const { return m_currentBasePoints; } + + int32 GetAuraMaxDuration() const { return m_maxduration; } + void SetAuraMaxDuration(int32 duration) { m_maxduration = duration; } + int32 GetAuraDuration() const { return m_duration; } + void SetAuraDuration(int32 duration) { m_duration = duration; } + time_t GetAuraApplyTime() { return m_applyTime; } + void UpdateAuraDuration(); + void SendAuraDurationForCaster(Player* caster); + + uint64 const& GetCasterGUID() const { return m_caster_guid; } + Unit* GetCaster() const; + Unit* GetTarget() const { return m_target; } + void SetTarget(Unit* target) { m_target = target; } + void SetLoadedState(uint64 caster_guid,int32 damage,int32 maxduration,int32 duration,int32 charges) + { + m_caster_guid = caster_guid; + m_modifier.m_amount = damage; + m_maxduration = maxduration; + m_duration = duration; + m_procCharges = charges; + } + + uint8 GetAuraSlot() const { return m_auraSlot; } + void SetAuraSlot(uint8 slot) { m_auraSlot = slot; } + void UpdateAuraCharges() + { + uint8 slot = GetAuraSlot(); + + // only aura inslot with charges and without stack limitation + if (slot < MAX_AURAS && m_procCharges >= 1 && GetSpellProto()->StackAmount==0) + SetAuraApplication(slot, m_procCharges - 1); + } + + bool IsPositive() { return m_positive; } + void SetNegative() { m_positive = false; } + void SetPositive() { m_positive = true; } + + bool IsPermanent() const { return m_permanent; } + bool IsAreaAura() const { return m_isAreaAura; } + bool IsPeriodic() const { return m_isPeriodic; } + bool IsTrigger() const { return m_isTrigger; } + bool IsPassive() const { return m_isPassive; } + bool IsPersistent() const { return m_isPersistent; } + bool IsDeathPersistent() const { return m_isDeathPersist; } + bool IsRemovedOnShapeLost() const { return m_isRemovedOnShapeLost; } + bool IsInUse() const { return m_in_use;} + + virtual void Update(uint32 diff); + void ApplyModifier(bool apply, bool Real = false); + + void _AddAura(); + void _RemoveAura(); + + void TriggerSpell(); + + bool IsUpdated() { return m_updated; } + void SetUpdated(bool val) { m_updated = val; } + void SetRemoveMode(AuraRemoveMode mode) { m_removeMode = mode; } + + int32 m_procCharges; + + virtual Unit* GetTriggerTarget() const { return m_target; } + + // add/remove SPELL_AURA_MOD_SHAPESHIFT (36) linked auras + void HandleShapeshiftBoosts(bool apply); + + // Allow Apply Aura Handler to modify and access m_AuraDRGroup + void setDiminishGroup(DiminishingGroup group) { m_AuraDRGroup = group; } + DiminishingGroup getDiminishGroup() const { return m_AuraDRGroup; } + + void PeriodicTick(); + void PeriodicDummyTick(); + protected: + Aura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); + + Modifier m_modifier; + SpellModifier *m_spellmod; + uint32 m_effIndex; + SpellEntry const *m_spellProto; + int32 m_currentBasePoints; // cache SpellEntry::EffectBasePoints and use for set custom base points + uint64 m_caster_guid; + Unit* m_target; + int32 m_maxduration; + int32 m_duration; + int32 m_timeCla; + uint64 m_castItemGuid; // it is NOT safe to keep a pointer to the item because it may get deleted + time_t m_applyTime; + + AuraRemoveMode m_removeMode; + + uint8 m_auraSlot; + + bool m_positive:1; + bool m_permanent:1; + bool m_isPeriodic:1; + bool m_isTrigger:1; + bool m_isAreaAura:1; + bool m_isPassive:1; + bool m_isPersistent:1; + bool m_isDeathPersist:1; + bool m_isRemovedOnShapeLost:1; + bool m_updated:1; + bool m_in_use:1; // true while in Aura::ApplyModifier call + + int32 m_periodicTimer; + uint32 m_PeriodicEventId; + DiminishingGroup m_AuraDRGroup; + private: + void UpdateSlotCounterAndDuration(bool add); + void CleanupTriggeredSpells(); + void SetAura(uint32 slot, bool remove) { m_target->SetUInt32Value(UNIT_FIELD_AURA + slot, remove ? 0 : GetId()); } + void SetAuraFlag(uint32 slot, bool add); + void SetAuraLevel(uint32 slot, uint32 level); + void SetAuraApplication(uint32 slot, int8 count); +}; + +class MANGOS_DLL_SPEC AreaAura : public Aura +{ + public: + AreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); + ~AreaAura(); + void Update(uint32 diff); + private: + float m_radius; + AreaAuraType m_areaAuraType; +}; + +class MANGOS_DLL_SPEC PersistentAreaAura : public Aura +{ + public: + PersistentAreaAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); + ~PersistentAreaAura(); + void Update(uint32 diff); +}; + +class MANGOS_DLL_SPEC SingleEnemyTargetAura : public Aura +{ + friend Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster, Item* castItem); + + public: + ~SingleEnemyTargetAura(); + Unit* GetTriggerTarget() const; + + protected: + SingleEnemyTargetAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); + uint64 m_casters_target_guid; +}; + +Aura* CreateAura(SpellEntry const* spellproto, uint32 eff, int32 *currentBasePoints, Unit *target, Unit *caster = NULL, Item* castItem = NULL); +#endif diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp new file mode 100644 index 00000000000..fa037fcdb24 --- /dev/null +++ b/src/game/SpellEffects.cpp @@ -0,0 +1,6090 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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" + +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::EffectUnused, // 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::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) + if(ihit->effectMask & (1<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 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; + } + } + 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_STUNDED) && 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::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.GetBattleGround(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 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 ) + { + if(BattleGround *bg = player->GetBattleGround())// in battleground + { + if( !player->IsMounted() && // not mounted + !player->HasStealthAura() && // not stealthed + !player->HasInvisibilityAura() && // not invisible + player->isAlive() ) // live player + { + // check if it's correct bg + if(bg && bg->GetTypeID() == BATTLEGROUND_AB) + bg->EventPlayerClickedOnFlag(player, gameObjTarget); + + return; + } + } + } + else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND) + { + 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 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<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 > 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(aur->GetId(),aur->GetCasterGUID())); + // Remove buff from list for prevent doubles + for (std::vector::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 >::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_STUNDED | 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(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<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; + } + } + + 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::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 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 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<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 > 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(aur->GetId(),aur->GetCasterGUID())); + + // Remove buff from list for prevent doubles + for (std::vector::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 >::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/SpellHandler.cpp b/src/game/SpellHandler.cpp new file mode 100644 index 00000000000..7bad99b38b5 --- /dev/null +++ b/src/game/SpellHandler.cpp @@ -0,0 +1,460 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DBCStores.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 "SpellAuras.h" +#include "BattleGround.h" +#include "MapManager.h" +#include "ScriptCalls.h" +#include "Totem.h" + +void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket) +{ + // TODO: add targets.read() check + CHECK_PACKET_SIZE(recvPacket,1+1+1+1+8); + + Player* pUser = _player; + uint8 bagIndex, slot; + uint8 spell_count; // number of spells at item, not used + uint8 cast_count; // next cast if exists (single or not) + uint64 item_guid; + + recvPacket >> bagIndex >> slot >> spell_count >> cast_count >> item_guid; + + Item *pItem = pUser->GetItemByPos(bagIndex, slot); + if(!pItem) + { + pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); + return; + } + + sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, spell_count: %u , cast_count: %u, Item: %u, data length = %i", bagIndex, slot, spell_count, cast_count, pItem->GetEntry(), recvPacket.size()); + + ItemPrototype const *proto = pItem->GetProto(); + if(!proto) + { + pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL ); + return; + } + + // some item classes can be used only in equipped state + if(proto->InventoryType != INVTYPE_NON_EQUIP && !pItem->IsEquipped()) + { + pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL ); + return; + } + + uint8 msg = pUser->CanUseItem(pItem); + if( msg != EQUIP_ERR_OK ) + { + pUser->SendEquipError( msg, pItem, NULL ); + return; + } + + // only allow conjured consumable, bandage, poisons (all should have the 2^21 item flag set in DB) + if( proto->Class == ITEM_CLASS_CONSUMABLE && + !(proto->Flags & ITEM_FLAGS_USEABLE_IN_ARENA) && + pUser->InArena()) + { + pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL); + return; + } + + if (pUser->isInCombat()) + { + for(int i = 0; i < 5; ++i) + { + if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(proto->Spells[i].SpellId)) + { + if (IsNonCombatSpell(spellInfo)) + { + pUser->SendEquipError(EQUIP_ERR_NOT_IN_COMBAT,pItem,NULL); + 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_USE || pItem->GetProto()->Bonding == BIND_WHEN_PICKED_UP || pItem->GetProto()->Bonding == BIND_QUEST_ITEM ) + { + if (!pItem->IsSoulBound()) + { + pItem->SetState(ITEM_CHANGED, pUser); + pItem->SetBinding( true ); + } + } + + SpellCastTargets targets; + if(!targets.read(&recvPacket, pUser)) + return; + + //Note: If script stop casting it must send appropriate data to client to prevent stuck item in gray state. + if(!Script->ItemUse(pUser,pItem,targets)) + { + // no script or script not process request by self + + // special learning case + if(pItem->GetProto()->Spells[0].SpellId==SPELL_ID_GENERIC_LEARN) + { + uint32 learning_spell_id = pItem->GetProto()->Spells[1].SpellId; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(SPELL_ID_GENERIC_LEARN); + if(!spellInfo) + { + sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, SPELL_ID_GENERIC_LEARN); + pUser->SendEquipError(EQUIP_ERR_NONE,pItem,NULL); + return; + } + + Spell *spell = new Spell(pUser, spellInfo, false); + spell->m_CastItem = pItem; + spell->m_cast_count = cast_count; //set count of casts + spell->m_currentBasePoints[0] = learning_spell_id; + spell->prepare(&targets); + return; + } + + // use triggered flag only for items with many spell casts and for not first cast + int count = 0; + + for(int i = 0; i < 5; ++i) + { + _Spell const& spellData = pItem->GetProto()->Spells[i]; + + // no spell + if(!spellData.SpellId) + continue; + + // wrong triggering type + if( spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_USE && spellData.SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) + continue; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellData.SpellId); + if(!spellInfo) + { + sLog.outError("Item (Entry: %u) in have wrong spell id %u, ignoring ",proto->ItemId, spellData.SpellId); + continue; + } + + Spell *spell = new Spell(pUser, spellInfo, (count > 0)); + spell->m_CastItem = pItem; + spell->m_cast_count = cast_count; //set count of casts + spell->prepare(&targets); + + ++count; + } + } +} + +#define OPEN_CHEST 11437 +#define OPEN_SAFE 11535 +#define OPEN_CAGE 11792 +#define OPEN_BOOTY_CHEST 5107 +#define OPEN_STRONGBOX 8517 + +void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,1+1); + + sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",recvPacket.size()); + + Player* pUser = _player; + uint8 bagIndex, slot; + + recvPacket >> bagIndex >> slot; + + sLog.outDetail("bagIndex: %u, slot: %u",bagIndex,slot); + + Item *pItem = pUser->GetItemByPos(bagIndex, slot); + if(!pItem) + { + pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); + return; + } + + ItemPrototype const *proto = pItem->GetProto(); + if(!proto) + { + pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, pItem, NULL ); + return; + } + + // locked item + uint32 lockId = proto->LockID; + if(lockId) + { + LockEntry const *lockInfo = sLockStore.LookupEntry(lockId); + + if (!lockInfo) + { + pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL ); + sLog.outError( "WORLD::OpenItem: item [guid = %u] has an unknown lockId: %u!", pItem->GetGUIDLow() , lockId); + return; + } + + // required picklocking + if(lockInfo->requiredlockskill || lockInfo->requiredminingskill) + { + pUser->SendEquipError(EQUIP_ERR_ITEM_LOCKED, pItem, NULL ); + return; + } + } + + if(pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))// wrapped? + { + QueryResult *result = CharacterDatabase.PQuery("SELECT entry, flags FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow()); + if (result) + { + Field *fields = result->Fetch(); + uint32 entry = fields[0].GetUInt32(); + uint32 flags = fields[1].GetUInt32(); + + pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0); + pItem->SetUInt32Value(OBJECT_FIELD_ENTRY, entry); + pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags); + pItem->SetState(ITEM_CHANGED, pUser); + delete result; + } + else + { + sLog.outError("Wrapped item %u don't have record in character_gifts table and will deleted", pItem->GetGUIDLow()); + pUser->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); + return; + } + CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE item_guid = '%u'", pItem->GetGUIDLow()); + } + else + pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE); +} + +void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + uint64 guid; + uint32 spellId = OPEN_CHEST; + + recv_data >> guid; + + sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", guid); + GameObject *obj = ObjectAccessor::GetGameObject(*_player, guid); + + if(!obj) + return; + + if (Script->GOHello(_player, obj)) + return; + + obj->Use(_player); +} + +void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,4+1+2); + + uint32 spellId; + uint8 cast_count; + recvPacket >> spellId; + recvPacket >> cast_count; + + sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u data length = %i", + spellId, cast_count, recvPacket.size()); + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); + + if(!spellInfo) + { + sLog.outError("WORLD: unknown spell id %u", spellId); + return; + } + + // not have spell or spell passive and not casted by client + if ( !_player->HasSpell (spellId) || IsPassiveSpell(spellId) ) + { + //cheater? kick? ban? + return; + } + + // client provided targets + SpellCastTargets targets; + if(!targets.read(&recvPacket,_player)) + return; + + // auto-selection buff level base at target level (in spellInfo) + if(targets.getUnitTarget()) + { + SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(spellInfo,targets.getUnitTarget()->getLevel()); + + // if rank not found then function return NULL but in explicit cast case original spell can be casted and later failed with appropriate error message + if(actualSpellInfo) + spellInfo = actualSpellInfo; + } + + Spell *spell = new Spell(_player, spellInfo, false); + spell->m_cast_count = cast_count; //set count of casts + spell->prepare(&targets); +} + +void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,4); + + uint32 spellId; + recvPacket >> spellId; + + //FIXME: hack, ignore unexpected client cancel Deadly Throw cast + if(spellId==26679) + return; + + if(_player->IsNonMeleeSpellCasted(false)) + _player->InterruptNonMeleeSpells(false,spellId); +} + +void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,4); + + uint32 spellId; + recvPacket >> spellId; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); + if (!spellInfo) + return; + + // not allow remove non positive spells and spells with attr SPELL_ATTR_CANT_CANCEL + if(!IsPositiveSpell(spellId) || (spellInfo->Attributes & SPELL_ATTR_CANT_CANCEL)) + return; + + _player->RemoveAurasDueToSpellByCancel(spellId); + + if (spellId == 2584) // Waiting to resurrect spell cancel, we must remove player from resurrect queue + { + BattleGround *bg = _player->GetBattleGround(); + if(!bg) + return; + bg->RemovePlayerFromResurrectQueue(_player->GetGUID()); + } +} + +void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 8+4); + + uint64 guid; + uint32 spellId; + + recvPacket >> guid; + recvPacket >> spellId; + + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId ); + if(!spellInfo) + { + sLog.outError("WORLD: unknown PET spell id %u", spellId); + return; + } + + Creature* pet=ObjectAccessor::GetCreatureOrPet(*_player,guid); + + if(!pet) + { + sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid)) ); + return; + } + + if(pet != GetPlayer()->GetPet() && pet != GetPlayer()->GetCharm()) + { + sLog.outError( "HandlePetCancelAura.Pet %u isn't pet of player %s", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() ); + return; + } + + if(!pet->isAlive()) + { + pet->SendPetActionFeedback(FEEDBACK_PET_DEAD); + return; + } + + pet->RemoveAurasDueToSpell(spellId); + + pet->AddCreatureSpellCooldown(spellId); +} + +void WorldSession::HandleCancelGrowthAuraOpcode( WorldPacket& /*recvPacket*/) +{ + // nothing do +} + +void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket*/) +{ + // may be better send SMSG_CANCEL_AUTO_REPEAT? + // cancel and prepare for deleting + _player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); +} + +/// \todo Complete HandleCancelChanneling function +void WorldSession::HandleCancelChanneling( WorldPacket & /*recv_data */) +{ + /* + CHECK_PACKET_SIZE(recv_data, 4); + + uint32 spellid; + recv_data >> spellid; + */ +} + +void WorldSession::HandleTotemDestroy( WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket, 1); + + uint8 slotId; + + recvPacket >> slotId; + + if (slotId >= MAX_TOTEM) + return; + + if(!_player->m_TotemSlot[slotId]) + return; + + Creature* totem = ObjectAccessor::GetCreature(*_player,_player->m_TotemSlot[slotId]); + if(totem && totem->isTotem()) + ((Totem*)totem)->UnSummon(); +} + +void WorldSession::HandleSelfResOpcode( WorldPacket & /*recv_data*/ ) +{ + sLog.outDebug("WORLD: CMSG_SELF_RES"); // empty opcode + + if(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL)) + { + SpellEntry const *spellInfo = sSpellStore.LookupEntry(_player->GetUInt32Value(PLAYER_SELF_RES_SPELL)); + if(spellInfo) + _player->CastSpell(_player,spellInfo,false,0); + + _player->SetUInt32Value(PLAYER_SELF_RES_SPELL, 0); + } +} diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp new file mode 100644 index 00000000000..fc8c5d43b7b --- /dev/null +++ b/src/game/SpellMgr.cpp @@ -0,0 +1,2268 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "SpellMgr.h" +#include "ObjectMgr.h" +#include "SpellAuraDefines.h" +#include "ProgressBar.h" +#include "Database/DBCStores.h" +#include "World.h" +#include "Chat.h" +#include "Spell.h" + +SpellMgr::SpellMgr() +{ +} + +SpellMgr::~SpellMgr() +{ +} + +SpellMgr& SpellMgr::Instance() +{ + static SpellMgr spellMgr; + return spellMgr; +} + +int32 GetSpellDuration(SpellEntry const *spellInfo) +{ + if(!spellInfo) + return 0; + SpellDurationEntry const *du = sSpellDurationStore.LookupEntry(spellInfo->DurationIndex); + if(!du) + return 0; + return (du->Duration[0] == -1) ? -1 : abs(du->Duration[0]); +} + +int32 GetSpellMaxDuration(SpellEntry const *spellInfo) +{ + if(!spellInfo) + return 0; + SpellDurationEntry const *du = sSpellDurationStore.LookupEntry(spellInfo->DurationIndex); + if(!du) + return 0; + return (du->Duration[2] == -1) ? -1 : abs(du->Duration[2]); +} + +uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell) +{ + SpellCastTimesEntry const *spellCastTimeEntry = sSpellCastTimesStore.LookupEntry(spellInfo->CastingTimeIndex); + + // not all spells have cast time index and this is all is pasiive abilities + if(!spellCastTimeEntry) + return 0; + + int32 castTime = spellCastTimeEntry->CastTime; + + if (spell) + { + if(Player* modOwner = spell->GetCaster()->GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell); + + if( !(spellInfo->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_UNK5)) ) + castTime = int32(castTime * spell->GetCaster()->GetFloatValue(UNIT_MOD_CAST_SPEED)); + else + { + if (spell->IsRangedSpell() && !spell->IsAutoRepeat()) + castTime = int32(castTime * spell->GetCaster()->m_modAttackSpeedPct[RANGED_ATTACK]); + } + } + + if (spellInfo->Attributes & SPELL_ATTR_RANGED && (!spell || !(spell->IsAutoRepeat()))) + castTime += 500; + + return (castTime > 0) ? uint32(castTime) : 0; +} + +bool IsPassiveSpell(uint32 spellId) +{ + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); + if (!spellInfo) + return false; + return (spellInfo->Attributes & SPELL_ATTR_PASSIVE) != 0; +} + +bool IsNoStackAuraDueToAura(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2) +{ + SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1); + SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2); + if(!spellInfo_1 || !spellInfo_2) return false; + if(spellInfo_1->Id == spellId_2) return false; + + if (spellInfo_1->Effect[effIndex_1] != spellInfo_2->Effect[effIndex_2] || + spellInfo_1->EffectItemType[effIndex_1] != spellInfo_2->EffectItemType[effIndex_2] || + spellInfo_1->EffectMiscValue[effIndex_1] != spellInfo_2->EffectMiscValue[effIndex_2] || + spellInfo_1->EffectApplyAuraName[effIndex_1] != spellInfo_2->EffectApplyAuraName[effIndex_2]) + return false; + + return true; +} + +int32 CompareAuraRanks(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2) +{ + SpellEntry const*spellInfo_1 = sSpellStore.LookupEntry(spellId_1); + SpellEntry const*spellInfo_2 = sSpellStore.LookupEntry(spellId_2); + if(!spellInfo_1 || !spellInfo_2) return 0; + if (spellId_1 == spellId_2) return 0; + + int32 diff = spellInfo_1->EffectBasePoints[effIndex_1] - spellInfo_2->EffectBasePoints[effIndex_2]; + if (spellInfo_1->EffectBasePoints[effIndex_1]+1 < 0 && spellInfo_2->EffectBasePoints[effIndex_2]+1 < 0) return -diff; + else return diff; +} + +SpellSpecific GetSpellSpecific(uint32 spellId) +{ + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); + if(!spellInfo) + return SPELL_NORMAL; + + switch(spellInfo->SpellFamilyName) + { + case SPELLFAMILY_MAGE: + { + // family flags 18(Molten), 25(Frost/Ice), 28(Mage) + if (spellInfo->SpellFamilyFlags & 0x12040000) + return SPELL_MAGE_ARMOR; + + if ((spellInfo->SpellFamilyFlags & 0x1000000) && spellInfo->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE) + return SPELL_MAGE_POLYMORPH; + + break; + } + case SPELLFAMILY_WARRIOR: + { + if (spellInfo->SpellFamilyFlags & 0x00008000010000LL) + return SPELL_POSITIVE_SHOUT; + + break; + } + case SPELLFAMILY_WARLOCK: + { + // only warlock curses have this + if (spellInfo->Dispel == DISPEL_CURSE) + return SPELL_CURSE; + + // family flag 37 (only part spells have family name) + if (spellInfo->SpellFamilyFlags & 0x2000000000LL) + return SPELL_WARLOCK_ARMOR; + + break; + } + case SPELLFAMILY_HUNTER: + { + // only hunter stings have this + if (spellInfo->Dispel == DISPEL_POISON) + return SPELL_STING; + + break; + } + case SPELLFAMILY_PALADIN: + { + if (IsSealSpell(spellInfo)) + return SPELL_SEAL; + + if (spellInfo->SpellFamilyFlags & 0x10000100LL) + return SPELL_BLESSING; + + if ((spellInfo->SpellFamilyFlags & 0x00000820180400LL) && (spellInfo->AttributesEx3 & 0x200)) + return SPELL_JUDGEMENT; + + for (int i = 0; i < 3; i++) + { + // only paladin auras have this + if (spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY) + return SPELL_AURA; + } + break; + } + case SPELLFAMILY_SHAMAN: + { + if (IsElementalShield(spellInfo)) + return SPELL_ELEMENTAL_SHIELD; + + break; + } + + case SPELLFAMILY_POTION: + return spellmgr.GetSpellElixirSpecific(spellInfo->Id); + } + + // only warlock armor/skin have this (in additional to family cases) + if( spellInfo->SpellVisual == 130 && spellInfo->SpellIconID == 89) + { + return SPELL_WARLOCK_ARMOR; + } + + // only hunter aspects have this (but not all aspects in hunter family) + if( spellInfo->activeIconID == 122 && (GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_NATURE) && + (spellInfo->Attributes & 0x50000) != 0 && (spellInfo->Attributes & 0x9000010) == 0) + { + return SPELL_ASPECT; + } + + for(int i = 0; i < 3; i++) + if( spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA && ( + spellInfo->EffectApplyAuraName[i] == SPELL_AURA_TRACK_CREATURES || + spellInfo->EffectApplyAuraName[i] == SPELL_AURA_TRACK_RESOURCES || + spellInfo->EffectApplyAuraName[i] == SPELL_AURA_TRACK_STEALTHED ) ) + return SPELL_TRACKER; + + // elixirs can have different families, but potion most ofc. + if(SpellSpecific sp = spellmgr.GetSpellElixirSpecific(spellInfo->Id)) + return sp; + + return SPELL_NORMAL; +} + +bool IsSingleFromSpellSpecificPerCaster(uint32 spellSpec1,uint32 spellSpec2) +{ + switch(spellSpec1) + { + case SPELL_SEAL: + case SPELL_BLESSING: + case SPELL_AURA: + case SPELL_STING: + case SPELL_CURSE: + case SPELL_ASPECT: + case SPELL_TRACKER: + case SPELL_WARLOCK_ARMOR: + case SPELL_MAGE_ARMOR: + case SPELL_MAGE_POLYMORPH: + case SPELL_POSITIVE_SHOUT: + case SPELL_JUDGEMENT: + return spellSpec1==spellSpec2; + case SPELL_BATTLE_ELIXIR: + return spellSpec2==SPELL_BATTLE_ELIXIR + || spellSpec2==SPELL_FLASK_ELIXIR; + case SPELL_GUARDIAN_ELIXIR: + return spellSpec2==SPELL_GUARDIAN_ELIXIR + || spellSpec2==SPELL_FLASK_ELIXIR; + case SPELL_FLASK_ELIXIR: + return spellSpec2==SPELL_BATTLE_ELIXIR + || spellSpec2==SPELL_GUARDIAN_ELIXIR + || spellSpec2==SPELL_FLASK_ELIXIR; + default: + return false; + } +} + +bool IsPositiveTarget(uint32 targetA, uint32 targetB) +{ + // non-positive targets + switch(targetA) + { + case TARGET_CHAIN_DAMAGE: + case TARGET_ALL_ENEMY_IN_AREA: + case TARGET_ALL_ENEMY_IN_AREA_INSTANT: + case TARGET_IN_FRONT_OF_CASTER: + case TARGET_ALL_ENEMY_IN_AREA_CHANNELED: + case TARGET_CURRENT_ENEMY_COORDINATES: + case TARGET_SINGLE_ENEMY: + return false; + case TARGET_ALL_AROUND_CASTER: + return (targetB == TARGET_ALL_PARTY || targetB == TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER); + default: + break; + } + if (targetB) + return IsPositiveTarget(targetB, 0); + return true; +} + +bool IsPositiveEffect(uint32 spellId, uint32 effIndex) +{ + SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); + if (!spellproto) return false; + + switch(spellId) + { + case 23333: // BG spell + case 23335: // BG spell + case 34976: // BG spell + return true; + case 28441: // not positive dummy spell + case 37675: // Chaos Blast + return false; + } + + switch(spellproto->Effect[effIndex]) + { + // always positive effects (check before target checks that provided non-positive result in some case for positive effects) + case SPELL_EFFECT_HEAL: + case SPELL_EFFECT_LEARN_SPELL: + case SPELL_EFFECT_SKILL_STEP: + case SPELL_EFFECT_HEAL_PCT: + case SPELL_EFFECT_ENERGIZE_PCT: + return true; + + // non-positive aura use + case SPELL_EFFECT_APPLY_AURA: + case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: + { + switch(spellproto->EffectApplyAuraName[effIndex]) + { + case SPELL_AURA_DUMMY: + { + // dummy aura can be positive or negative dependent from casted spell + switch(spellproto->Id) + { + case 13139: // net-o-matic special effect + case 23445: // evil twin + return false; + default: + break; + } + } break; + case SPELL_AURA_MOD_STAT: + case SPELL_AURA_MOD_DAMAGE_DONE: // dependent from bas point sign (negative -> negative) + case SPELL_AURA_MOD_HEALING_DONE: + { + if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0) + return false; + break; + } + case SPELL_AURA_ADD_TARGET_TRIGGER: + return true; + case SPELL_AURA_PERIODIC_TRIGGER_SPELL: + if(spellId != spellproto->EffectTriggerSpell[effIndex]) + { + uint32 spellTriggeredId = spellproto->EffectTriggerSpell[effIndex]; + SpellEntry const *spellTriggeredProto = sSpellStore.LookupEntry(spellTriggeredId); + + if(spellTriggeredProto) + { + // non-positive targets of main spell return early + for(int i = 0; i < 3; ++i) + { + // if non-positive trigger cast targeted to positive target this main cast is non-positive + // this will place this spell auras as debuffs + if(IsPositiveTarget(spellTriggeredProto->EffectImplicitTargetA[effIndex],spellTriggeredProto->EffectImplicitTargetB[effIndex]) && !IsPositiveEffect(spellTriggeredId,i)) + return false; + } + } + } + break; + case SPELL_AURA_PROC_TRIGGER_SPELL: + // many positive auras have negative triggered spells at damage for example and this not make it negative (it can be canceled for example) + break; + case SPELL_AURA_MOD_STUN: //have positive and negative spells, we can't sort its correctly at this moment. + if(effIndex==0 && spellproto->Effect[1]==0 && spellproto->Effect[2]==0) + return false; // but all single stun aura spells is negative + + // Petrification + if(spellproto->Id == 17624) + return false; + break; + case SPELL_AURA_MOD_ROOT: + case SPELL_AURA_MOD_SILENCE: + case SPELL_AURA_GHOST: + case SPELL_AURA_PERIODIC_LEECH: + case SPELL_AURA_MOD_PACIFY_SILENCE: + case SPELL_AURA_MOD_STALKED: + case SPELL_AURA_PERIODIC_DAMAGE_PERCENT: + return false; + case SPELL_AURA_PERIODIC_DAMAGE: // used in positive spells also. + // part of negative spell if casted at self (prevent cancel) + if(spellproto->EffectImplicitTargetA[effIndex] == TARGET_SELF) + return false; + break; + case SPELL_AURA_MOD_DECREASE_SPEED: // used in positive spells also + // part of positive spell if casted at self + if(spellproto->EffectImplicitTargetA[effIndex] != TARGET_SELF) + return false; + // but not this if this first effect (don't found batter check) + if(spellproto->Attributes & 0x4000000 && effIndex==0) + return false; + break; + case SPELL_AURA_TRANSFORM: + // some spells negative + switch(spellproto->Id) + { + case 36897: // Transporter Malfunction (race mutation to horde) + case 36899: // Transporter Malfunction (race mutation to alliance) + return false; + } + break; + case SPELL_AURA_MOD_SCALE: + // some spells negative + switch(spellproto->Id) + { + case 36900: // Soul Split: Evil! + case 36901: // Soul Split: Good + case 36893: // Transporter Malfunction (decrease size case) + case 36895: // Transporter Malfunction (increase size case) + return false; + } + break; + case SPELL_AURA_MECHANIC_IMMUNITY: + { + // non-positive immunities + switch(spellproto->EffectMiscValue[effIndex]) + { + case MECHANIC_BANDAGE: + case MECHANIC_SHIELD: + case MECHANIC_MOUNT: + case MECHANIC_INVULNERABILITY: + return false; + default: + break; + } + } break; + case SPELL_AURA_ADD_FLAT_MODIFIER: // mods + case SPELL_AURA_ADD_PCT_MODIFIER: + { + // non-positive mods + switch(spellproto->EffectMiscValue[effIndex]) + { + case SPELLMOD_COST: // dependent from bas point sign (negative -> positive) + if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) > 0) + return false; + break; + default: + break; + } + } break; + case SPELL_AURA_MOD_HEALING_PCT: + if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0) + return false; + break; + case SPELL_AURA_MOD_SKILL: + if(spellproto->EffectBasePoints[effIndex]+int32(spellproto->EffectBaseDice[effIndex]) < 0) + return false; + break; + case SPELL_AURA_FORCE_REACTION: + if(spellproto->Id==42792) // Recently Dropped Flag (prevent cancel) + return false; + break; + default: + break; + } + break; + } + default: + break; + } + + // non-positive targets + if(!IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex])) + return false; + + // AttributesEx check + if(spellproto->AttributesEx & (1<<7)) + return false; + + // ok, positive + return true; +} + +bool IsPositiveSpell(uint32 spellId) +{ + SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); + if (!spellproto) return false; + + // spells with atleast one negative effect are considered negative + // some self-applied spells have negative effects but in self casting case negative check ignored. + for (int i = 0; i < 3; i++) + if (!IsPositiveEffect(spellId, i)) + return false; + return true; +} + +bool IsSingleTargetSpell(SpellEntry const *spellInfo) +{ + // all other single target spells have if it has AttributesEx5 + if ( spellInfo->AttributesEx5 & SPELL_ATTR_EX5_SINGLE_TARGET_SPELL ) + return true; + + // TODO - need found Judgements rule + switch(GetSpellSpecific(spellInfo->Id)) + { + case SPELL_JUDGEMENT: + return true; + } + + // single target triggered spell. + // Not real client side single target spell, but it' not triggered until prev. aura expired. + // This is allow store it in single target spells list for caster for spell proc checking + if(spellInfo->Id==38324) // Regeneration (triggered by 38299 (HoTs on Heals)) + return true; + + return false; +} + +bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2) +{ + // TODO - need better check + // Equal icon and spellfamily + if( spellInfo1->SpellFamilyName == spellInfo2->SpellFamilyName && + spellInfo1->SpellIconID == spellInfo2->SpellIconID ) + return true; + + // TODO - need found Judgements rule + SpellSpecific spec1 = GetSpellSpecific(spellInfo1->Id); + // spell with single target specific types + switch(spec1) + { + case SPELL_JUDGEMENT: + if(GetSpellSpecific(spellInfo2->Id) == spec1) + return true; + break; + } + + return false; +} + +uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) +{ + // talents that learn spells can have stance requirements that need ignore + // (this requirement only for client-side stance show in talent description) + if( GetTalentSpellCost(spellInfo->Id) > 0 && + (spellInfo->Effect[0]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[1]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[2]==SPELL_EFFECT_LEARN_SPELL) ) + return 0; + + uint32 stanceMask = (form ? 1 << (form - 1) : 0); + + if (stanceMask & spellInfo->StancesNot) // can explicitly not be casted in this stance + return SPELL_FAILED_NOT_SHAPESHIFT; + + if (stanceMask & spellInfo->Stances) // can explicitly be casted in this stance + return 0; + + bool actAsShifted = false; + if (form > 0) + { + SpellShapeshiftEntry const *shapeInfo = sSpellShapeshiftStore.LookupEntry(form); + if (!shapeInfo) + { + sLog.outError("GetErrorAtShapeshiftedCast: unknown shapeshift %u", form); + return 0; + } + actAsShifted = !(shapeInfo->flags1 & 1); // shapeshift acts as normal form for spells + } + + if(actAsShifted) + { + if (spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) // not while shapeshifted + return SPELL_FAILED_NOT_SHAPESHIFT; + else if (spellInfo->Stances != 0) // needs other shapeshift + return SPELL_FAILED_ONLY_SHAPESHIFT; + } + else + { + // needs shapeshift + if(!(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT) && spellInfo->Stances != 0) + return SPELL_FAILED_ONLY_SHAPESHIFT; + } + + return 0; +} + +void SpellMgr::LoadSpellTargetPositions() +{ + mSpellTargetPositions.clear(); // need for reload case + + uint32 count = 0; + + // 0 1 2 3 4 5 + QueryResult *result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM spell_target_position"); + if( !result ) + { + + barGoLink bar( 1 ); + + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u spell target coordinates", count ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field *fields = result->Fetch(); + + bar.step(); + + ++count; + + uint32 Spell_ID = fields[0].GetUInt32(); + + SpellTargetPosition st; + + st.target_mapId = fields[1].GetUInt32(); + st.target_X = fields[2].GetFloat(); + st.target_Y = fields[3].GetFloat(); + st.target_Z = fields[4].GetFloat(); + st.target_Orientation = fields[5].GetFloat(); + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(Spell_ID); + if(!spellInfo) + { + sLog.outErrorDb("Spell (ID:%u) listed in `spell_target_position` does not exist.",Spell_ID); + continue; + } + + bool found = false; + for(int i = 0; i < 3; ++i) + { + if( spellInfo->EffectImplicitTargetA[i]==TARGET_TABLE_X_Y_Z_COORDINATES || spellInfo->EffectImplicitTargetB[i]==TARGET_TABLE_X_Y_Z_COORDINATES ) + { + found = true; + break; + } + } + if(!found) + { + sLog.outErrorDb("Spell (Id: %u) listed in `spell_target_position` does not have target TARGET_TABLE_X_Y_Z_COORDINATES (17).",Spell_ID); + continue; + } + + MapEntry const* mapEntry = sMapStore.LookupEntry(st.target_mapId); + if(!mapEntry) + { + sLog.outErrorDb("Spell (ID:%u) target map (ID: %u) does not exist in `Map.dbc`.",Spell_ID,st.target_mapId); + continue; + } + + if(st.target_X==0 && st.target_Y==0 && st.target_Z==0) + { + sLog.outErrorDb("Spell (ID:%u) target coordinates not provided.",Spell_ID); + continue; + } + + mSpellTargetPositions[Spell_ID] = st; + + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u spell teleport coordinates", count ); +} + +void SpellMgr::LoadSpellAffects() +{ + mSpellAffectMap.clear(); // need for reload case + + uint32 count = 0; + + // 0 1 2 + QueryResult *result = WorldDatabase.Query("SELECT entry, effectId, SpellFamilyMask FROM spell_affect"); + if( !result ) + { + + barGoLink bar( 1 ); + + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u spell affect definitions", count ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field *fields = result->Fetch(); + + bar.step(); + + uint16 entry = fields[0].GetUInt16(); + uint8 effectId = fields[1].GetUInt8(); + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(entry); + + if (!spellInfo) + { + sLog.outErrorDb("Spell %u listed in `spell_affect` does not exist", entry); + continue; + } + + if (effectId >= 3) + { + sLog.outErrorDb("Spell %u listed in `spell_affect` have invalid effect index (%u)", entry,effectId); + continue; + } + + if( spellInfo->Effect[effectId] != SPELL_EFFECT_APPLY_AURA || + spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_FLAT_MODIFIER && + spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_PCT_MODIFIER && + spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_TARGET_TRIGGER ) + { + sLog.outErrorDb("Spell %u listed in `spell_affect` have not SPELL_AURA_ADD_FLAT_MODIFIER (%u) or SPELL_AURA_ADD_PCT_MODIFIER (%u) or SPELL_AURA_ADD_TARGET_TRIGGER (%u) for effect index (%u)", entry,SPELL_AURA_ADD_FLAT_MODIFIER,SPELL_AURA_ADD_PCT_MODIFIER,SPELL_AURA_ADD_TARGET_TRIGGER,effectId); + continue; + } + + uint64 spellAffectMask = fields[2].GetUInt64(); + + // Spell.dbc have own data for low part of SpellFamilyMask + if( spellInfo->EffectItemType[effectId]) + { + if(spellInfo->EffectItemType[effectId] == spellAffectMask) + { + sLog.outErrorDb("Spell %u listed in `spell_affect` have redundant (same with EffectItemType%d) data for effect index (%u) and not needed, skipped.", entry,effectId+1,effectId); + continue; + } + + // 24429 have wrong data in EffectItemType and overwrites by DB, possible bug in client + if(spellInfo->Id!=24429 && spellInfo->EffectItemType[effectId] != spellAffectMask) + { + sLog.outErrorDb("Spell %u listed in `spell_affect` have different low part from EffectItemType%d for effect index (%u) and not needed, skipped.", entry,effectId+1,effectId); + continue; + } + } + + mSpellAffectMap.insert(SpellAffectMap::value_type((entry<<8) + effectId,spellAffectMask)); + + ++count; + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u spell affect definitions", count ); + + /* + // Commented for now, as it still produces many errors (still quite many spells miss spell_affect) + for (uint32 id = 0; id < sSpellStore.GetNumRows(); ++id) + { + SpellEntry const* spellInfo = sSpellStore.LookupEntry(id); + if (!spellInfo) + continue; + + for (int effectId = 0; effectId < 3; ++effectId) + { + if( spellInfo->Effect[effectId] != SPELL_EFFECT_APPLY_AURA || + (spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_FLAT_MODIFIER && + spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_PCT_MODIFIER && + spellInfo->EffectApplyAuraName[effectId] != SPELL_AURA_ADD_TARGET_TRIGGER) ) + continue; + + if(spellInfo->EffectItemType[effectId] != 0) + continue; + + if(mSpellAffectMap.find((id<<8) + effectId) != mSpellAffectMap.end()) + continue; + + sLog.outErrorDb("Spell %u (%s) misses spell_affect for effect %u",id,spellInfo->SpellName[sWorld.GetDBClang()], effectId); + } + } + */ +} + +bool SpellMgr::IsAffectedBySpell(SpellEntry const *spellInfo, uint32 spellId, uint8 effectId, uint64 familyFlags) const +{ + // false for spellInfo == NULL + if (!spellInfo) + return false; + + SpellEntry const *affect_spell = sSpellStore.LookupEntry(spellId); + // false for affect_spell == NULL + if (!affect_spell) + return false; + + // False if spellFamily not equal + if (affect_spell->SpellFamilyName != spellInfo->SpellFamilyName) + return false; + + // If familyFlags == 0 + if (!familyFlags) + { + // Get it from spellAffect table + familyFlags = GetSpellAffectMask(spellId,effectId); + // false if familyFlags == 0 + if (!familyFlags) + return false; + } + + // true + if (familyFlags & spellInfo->SpellFamilyFlags) + return true; + + return false; +} + +void SpellMgr::LoadSpellProcEvents() +{ + mSpellProcEventMap.clear(); // need for reload case + + uint32 count = 0; + + // 0 1 2 3 4 5 6 7 8 + QueryResult *result = WorldDatabase.Query("SELECT entry, SchoolMask, Category, SkillID, SpellFamilyName, SpellFamilyMask, procFlags, ppmRate, cooldown FROM spell_proc_event"); + if( !result ) + { + + barGoLink bar( 1 ); + + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u spell proc event conditions", count ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field *fields = result->Fetch(); + + bar.step(); + + uint16 entry = fields[0].GetUInt16(); + + if (!sSpellStore.LookupEntry(entry)) + { + sLog.outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry); + continue; + } + + SpellProcEventEntry spe; + + spe.schoolMask = fields[1].GetUInt32(); + spe.category = fields[2].GetUInt32(); + spe.skillId = fields[3].GetUInt32(); + spe.spellFamilyName = fields[4].GetUInt32(); + spe.spellFamilyMask = fields[5].GetUInt64(); + spe.procFlags = fields[6].GetUInt32(); + spe.ppmRate = fields[7].GetFloat(); + spe.cooldown = fields[8].GetUInt32(); + + mSpellProcEventMap[entry] = spe; + + ++count; + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u spell proc event conditions", count ); + + /* + // Commented for now, as it still produces many errors (still quite many spells miss spell_proc_event) + for (uint32 id = 0; id < sSpellStore.GetNumRows(); ++id) + { + SpellEntry const* spellInfo = sSpellStore.LookupEntry(id); + if (!spellInfo) + continue; + + bool found = false; + for (int effectId = 0; effectId < 3; ++effectId) + { + // at this moment check only SPELL_AURA_PROC_TRIGGER_SPELL + if( spellInfo->EffectApplyAuraName[effectId] == SPELL_AURA_PROC_TRIGGER_SPELL ) + { + found = true; + break; + } + } + + if(!found) + continue; + + if(GetSpellProcEvent(id)) + continue; + + sLog.outErrorDb("Spell %u (%s) misses spell_proc_event",id,spellInfo->SpellName[sWorld.GetDBClang()]); + } + */ +} + +bool SpellMgr::IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, SpellEntry const * procSpell, uint32 procFlags ) +{ + if((procFlags & spellProcEvent->procFlags) == 0) + return false; + + // Additional checks in case spell cast/hit/crit is the event + // Check (if set) school, category, skill line, spell talent mask + if(spellProcEvent->schoolMask && (!procSpell || (GetSpellSchoolMask(procSpell) & spellProcEvent->schoolMask) == 0)) + return false; + if(spellProcEvent->category && (!procSpell || procSpell->Category != spellProcEvent->category)) + return false; + if(spellProcEvent->skillId) + { + if (!procSpell) + return false; + + SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(procSpell->Id); + SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(procSpell->Id); + + bool found = false; + for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) + { + if(_spell_idx->second->skillId == spellProcEvent->skillId) + { + found = true; + break; + } + } + if (!found) + return false; + } + if(spellProcEvent->spellFamilyName && (!procSpell || spellProcEvent->spellFamilyName != procSpell->SpellFamilyName)) + return false; + if(spellProcEvent->spellFamilyMask && (!procSpell || (spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags) == 0)) + return false; + + return true; +} + +void SpellMgr::LoadSpellElixirs() +{ + mSpellElixirs.clear(); // need for reload case + + uint32 count = 0; + + // 0 1 + QueryResult *result = WorldDatabase.Query("SELECT entry, mask FROM spell_elixir"); + if( !result ) + { + + barGoLink bar( 1 ); + + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u spell elixir definitions", count ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field *fields = result->Fetch(); + + bar.step(); + + uint16 entry = fields[0].GetUInt16(); + uint8 mask = fields[1].GetUInt8(); + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(entry); + + if (!spellInfo) + { + sLog.outErrorDb("Spell %u listed in `spell_elixir` does not exist", entry); + continue; + } + + mSpellElixirs[entry] = mask; + + ++count; + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u spell elixir definitions", count ); +} + +void SpellMgr::LoadSpellThreats() +{ + sSpellThreatStore.Free(); // for reload + + sSpellThreatStore.Load(); + + sLog.outString( ">> Loaded %u aggro generating spells", sSpellThreatStore.RecordCount ); + sLog.outString(); +} + +bool SpellMgr::IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const +{ + SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2); + if(!spellInfo_1 || !spellInfo_2) return false; + if(spellInfo_1->Id == spellId_2) return false; + + return GetFirstSpellInChain(spellInfo_1->Id)==GetFirstSpellInChain(spellId_2); +} + +bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo) +{ + if(spellInfo->powerType != POWER_MANA && spellInfo->powerType != POWER_HEALTH) + return false; + if(IsProfessionSpell(spellInfo->Id)) + return false; + + // All stance spells. if any better way, change it. + for (int i = 0; i < 3; i++) + { + // Paladin aura Spell + if(spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN + && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AREA_AURA_PARTY) + return false; + // Druid form Spell + if(spellInfo->SpellFamilyName == SPELLFAMILY_DRUID + && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA + && spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT) + return false; + // Rogue Stealth + if(spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE + && spellInfo->Effect[i]==SPELL_EFFECT_APPLY_AURA + && spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_SHAPESHIFT) + return false; + } + return true; +} + +bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) const +{ + SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1); + SpellEntry const *spellInfo_2 = sSpellStore.LookupEntry(spellId_2); + + if(!spellInfo_1 || !spellInfo_2) + return false; + + if(spellInfo_1->Id == spellId_2) + return false; + + //I think we don't check this correctly because i need a exception for spell: + //72,11327,18461...(called from 1856,1857...) Call Aura 16,31, after trigger another spell who call aura 77 and 77 remove 16 and 31, this should not happen. + if(spellInfo_2->SpellFamilyFlags == 2048) + return false; + + // Resurrection sickness + if((spellInfo_1->Id == SPELL_ID_PASSIVE_RESURRECTION_SICKNESS) != (spellInfo_2->Id==SPELL_ID_PASSIVE_RESURRECTION_SICKNESS)) + return false; + + // Specific spell family spells + switch(spellInfo_1->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: + switch(spellInfo_2->SpellFamilyName) + { + case SPELLFAMILY_GENERIC: // same family case + { + // Thunderfury + if( spellInfo_1->Id == 21992 && spellInfo_2->Id == 27648 || spellInfo_2->Id == 21992 && spellInfo_1->Id == 27648 ) + return false; + + // Lightning Speed (Mongoose) and Fury of the Crashing Waves (Tsunami Talisman) + if( spellInfo_1->Id == 28093 && spellInfo_2->Id == 42084 || + spellInfo_2->Id == 28093 && spellInfo_1->Id == 42084 ) + return false; + + // Soulstone Resurrection and Twisting Nether (resurrector) + if( spellInfo_1->SpellIconID == 92 && spellInfo_2->SpellIconID == 92 && ( + spellInfo_1->SpellVisual == 99 && spellInfo_2->SpellVisual == 0 || + spellInfo_2->SpellVisual == 99 && spellInfo_1->SpellVisual == 0 ) ) + return false; + + // Heart of the Wild and (Primal Instinct (Idol of Terror) triggering spell or Agility) + if( spellInfo_1->SpellIconID == 240 && spellInfo_2->SpellIconID == 240 && ( + spellInfo_1->SpellVisual == 0 && spellInfo_2->SpellVisual == 78 || + spellInfo_2->SpellVisual == 0 && spellInfo_1->SpellVisual == 78 ) ) + return false; + + // Personalized Weather (thunder effect should overwrite rainy aura) + if(spellInfo_1->SpellIconID == 2606 && spellInfo_2->SpellIconID == 2606) + return false; + + // Brood Affliction: Bronze + if( (spellInfo_1->Id == 23170 && spellInfo_2->Id == 23171) || + (spellInfo_2->Id == 23170 && spellInfo_1->Id == 23171) ) + return false; + + break; + } + case SPELLFAMILY_WARRIOR: + { + // Scroll of Protection and Defensive Stance (multi-family check) + if( spellInfo_1->SpellIconID == 276 && spellInfo_1->SpellVisual == 196 && spellInfo_2->Id == 71) + return false; + + // Improved Hamstring -> Hamstring (multi-family check) + if( (spellInfo_2->SpellFamilyFlags & 2) && spellInfo_1->Id == 23694 ) + return false; + + break; + } + case SPELLFAMILY_DRUID: + { + // Scroll of Stamina and Leader of the Pack (multi-family check) + if( spellInfo_1->SpellIconID == 312 && spellInfo_1->SpellVisual == 216 && spellInfo_2->Id == 24932 ) + return false; + + // Dragonmaw Illusion (multi-family check) + if (spellId_1 == 40216 && spellId_2 == 42016 ) + return false; + + break; + } + case SPELLFAMILY_ROGUE: + { + // Garrote-Silence -> Garrote (multi-family check) + if( spellInfo_1->SpellIconID == 498 && spellInfo_1->SpellVisual == 0 && spellInfo_2->SpellIconID == 498 ) + return false; + + break; + } + case SPELLFAMILY_HUNTER: + { + // Concussive Shot and Imp. Concussive Shot (multi-family check) + if( spellInfo_1->Id == 19410 && spellInfo_2->Id == 5116 ) + return false; + + // Improved Wing Clip -> Wing Clip (multi-family check) + if( (spellInfo_2->SpellFamilyFlags & 0x40) && spellInfo_1->Id == 19229 ) + return false; + break; + } + case SPELLFAMILY_PALADIN: + { + // Unstable Currents and other -> *Sanctity Aura (multi-family check) + if( spellInfo_2->SpellIconID==502 && spellInfo_1->SpellIconID==502 && spellInfo_1->SpellVisual==969 ) + return false; + + // *Band of Eternal Champion and Seal of Command(multi-family check) + if( spellId_1 == 35081 && spellInfo_2->SpellIconID==561 && spellInfo_2->SpellVisual==7992) + return false; + + break; + } + } + break; + case SPELLFAMILY_MAGE: + if( spellInfo_2->SpellFamilyName == SPELLFAMILY_MAGE ) + { + // Blizzard & Chilled (and some other stacked with blizzard spells + if( (spellInfo_1->SpellFamilyFlags & 0x80) && (spellInfo_2->SpellFamilyFlags & 0x100000) || + (spellInfo_2->SpellFamilyFlags & 0x80) && (spellInfo_1->SpellFamilyFlags & 0x100000) ) + return false; + + // Blink & Improved Blink + if( (spellInfo_1->SpellFamilyFlags & 0x0000000000010000LL) && (spellInfo_2->SpellVisual == 72 && spellInfo_2->SpellIconID == 1499) || + (spellInfo_2->SpellFamilyFlags & 0x0000000000010000LL) && (spellInfo_1->SpellVisual == 72 && spellInfo_1->SpellIconID == 1499) ) + return false; + } + // Detect Invisibility and Mana Shield (multi-family check) + if( spellInfo_2->Id == 132 && spellInfo_1->SpellIconID == 209 && spellInfo_1->SpellVisual == 968 ) + return false; + + // Combustion and Fire Protection Aura (multi-family check) + if( spellInfo_1->Id == 11129 && spellInfo_2->SpellIconID == 33 && spellInfo_2->SpellVisual == 321 ) + return false; + + break; + case SPELLFAMILY_WARLOCK: + if( spellInfo_2->SpellFamilyName == SPELLFAMILY_WARLOCK ) + { + // Siphon Life and Drain Life + if( spellInfo_1->SpellIconID == 152 && spellInfo_2->SpellIconID == 546 || + spellInfo_2->SpellIconID == 152 && spellInfo_1->SpellIconID == 546 ) + return false; + + //Corruption & Seed of corruption + if( spellInfo_1->SpellIconID == 313 && spellInfo_2->SpellIconID == 1932 || + spellInfo_2->SpellIconID == 313 && spellInfo_1->SpellIconID == 1932 ) + if(spellInfo_1->SpellVisual != 0 && spellInfo_2->SpellVisual != 0) + return true; // can't be stacked + + // Corruption and Unstable Affliction + if( spellInfo_1->SpellIconID == 313 && spellInfo_2->SpellIconID == 2039 || + spellInfo_2->SpellIconID == 313 && spellInfo_1->SpellIconID == 2039 ) + return false; + + // (Corruption or Unstable Affliction) and (Curse of Agony or Curse of Doom) + if( (spellInfo_1->SpellIconID == 313 || spellInfo_1->SpellIconID == 2039) && (spellInfo_2->SpellIconID == 544 || spellInfo_2->SpellIconID == 91) || + (spellInfo_2->SpellIconID == 313 || spellInfo_2->SpellIconID == 2039) && (spellInfo_1->SpellIconID == 544 || spellInfo_1->SpellIconID == 91) ) + return false; + } + // Detect Invisibility and Mana Shield (multi-family check) + if( spellInfo_1->Id == 132 && spellInfo_2->SpellIconID == 209 && spellInfo_2->SpellVisual == 968 ) + return false; + break; + case SPELLFAMILY_WARRIOR: + if( spellInfo_2->SpellFamilyName == SPELLFAMILY_WARRIOR ) + { + // Rend and Deep Wound + if( (spellInfo_1->SpellFamilyFlags & 0x20) && (spellInfo_2->SpellFamilyFlags & 0x1000000000LL) || + (spellInfo_2->SpellFamilyFlags & 0x20) && (spellInfo_1->SpellFamilyFlags & 0x1000000000LL) ) + return false; + + // Battle Shout and Rampage + if( (spellInfo_1->SpellIconID == 456 && spellInfo_2->SpellIconID == 2006) || + (spellInfo_2->SpellIconID == 456 && spellInfo_1->SpellIconID == 2006) ) + return false; + } + + // Hamstring -> Improved Hamstring (multi-family check) + if( (spellInfo_1->SpellFamilyFlags & 2) && spellInfo_2->Id == 23694 ) + return false; + + // Defensive Stance and Scroll of Protection (multi-family check) + if( spellInfo_1->Id == 71 && spellInfo_2->SpellIconID == 276 && spellInfo_2->SpellVisual == 196 ) + return false; + + // Bloodlust and Bloodthirst (multi-family check) + if( spellInfo_2->Id == 2825 && spellInfo_1->SpellIconID == 38 && spellInfo_1->SpellVisual == 0 ) + return false; + + break; + case SPELLFAMILY_PRIEST: + if( spellInfo_2->SpellFamilyName == SPELLFAMILY_PRIEST ) + { + //Devouring Plague and Shadow Vulnerability + if( (spellInfo_1->SpellFamilyFlags & 0x2000000) && (spellInfo_2->SpellFamilyFlags & 0x800000000LL) || + (spellInfo_2->SpellFamilyFlags & 0x2000000) && (spellInfo_1->SpellFamilyFlags & 0x800000000LL) ) + return false; + + //StarShards and Shadow Word: Pain + if( (spellInfo_1->SpellFamilyFlags & 0x200000) && (spellInfo_2->SpellFamilyFlags & 0x8000) || + (spellInfo_2->SpellFamilyFlags & 0x200000) && (spellInfo_1->SpellFamilyFlags & 0x8000) ) + return false; + } + break; + case SPELLFAMILY_DRUID: + if( spellInfo_2->SpellFamilyName == SPELLFAMILY_DRUID ) + { + //Omen of Clarity and Blood Frenzy + if( (spellInfo_1->SpellFamilyFlags == 0x0 && spellInfo_1->SpellIconID == 108) && (spellInfo_2->SpellFamilyFlags & 0x20000000000000LL) || + (spellInfo_2->SpellFamilyFlags == 0x0 && spellInfo_2->SpellIconID == 108) && (spellInfo_1->SpellFamilyFlags & 0x20000000000000LL) ) + return false; + + // Tree of Life (Shapeshift) and 34123 Tree of Life (Passive) + if ((spellId_1 == 33891 && spellId_2 == 34123) || + (spellId_2 == 33891 && spellId_1 == 34123)) + return false; + + // Wrath of Elune and Nature's Grace + if( spellInfo_1->Id == 16886 && spellInfo_2->Id == 46833 || spellInfo_2->Id == 16886 && spellInfo_1->Id == 46833 ) + return false; + + // Bear Rage (Feral T4 (2)) and Omen of Clarity + if( spellInfo_1->Id == 16864 && spellInfo_2->Id == 37306 || spellInfo_2->Id == 16864 && spellInfo_1->Id == 37306 ) + return false; + + // Cat Energy (Feral T4 (2)) and Omen of Clarity + if( spellInfo_1->Id == 16864 && spellInfo_2->Id == 37311 || spellInfo_2->Id == 16864 && spellInfo_1->Id == 37311 ) + return false; + } + + // Leader of the Pack and Scroll of Stamina (multi-family check) + if( spellInfo_1->Id == 24932 && spellInfo_2->SpellIconID == 312 && spellInfo_2->SpellVisual == 216 ) + return false; + + // Dragonmaw Illusion (multi-family check) + if (spellId_1 == 42016 && spellId_2 == 40216 ) + return false; + + break; + case SPELLFAMILY_ROGUE: + if( spellInfo_2->SpellFamilyName == SPELLFAMILY_ROGUE ) + { + // Master of Subtlety + if (spellId_1 == 31665 && spellId_2 == 31666 || spellId_1 == 31666 && spellId_2 == 31665 ) + return false; + } + + // Garrote -> Garrote-Silence (multi-family check) + if( spellInfo_1->SpellIconID == 498 && spellInfo_2->SpellIconID == 498 && spellInfo_2->SpellVisual == 0 ) + return false; + break; + case SPELLFAMILY_HUNTER: + if( spellInfo_2->SpellFamilyName == SPELLFAMILY_HUNTER ) + { + // Rapid Fire & Quick Shots + if( (spellInfo_1->SpellFamilyFlags & 0x20) && (spellInfo_2->SpellFamilyFlags & 0x20000000000LL) || + (spellInfo_2->SpellFamilyFlags & 0x20) && (spellInfo_1->SpellFamilyFlags & 0x20000000000LL) ) + return false; + + // Serpent Sting & (Immolation/Explosive Trap Effect) + if( (spellInfo_1->SpellFamilyFlags & 0x4) && (spellInfo_2->SpellFamilyFlags & 0x00000004000LL) || + (spellInfo_2->SpellFamilyFlags & 0x4) && (spellInfo_1->SpellFamilyFlags & 0x00000004000LL) ) + return false; + + // Bestial Wrath + if( spellInfo_1->SpellIconID == 1680 && spellInfo_2->SpellIconID == 1680 ) + return false; + } + + // Wing Clip -> Improved Wing Clip (multi-family check) + if( (spellInfo_1->SpellFamilyFlags & 0x40) && spellInfo_2->Id == 19229 ) + return false; + + // Concussive Shot and Imp. Concussive Shot (multi-family check) + if( spellInfo_2->Id == 19410 && spellInfo_1->Id == 5116 ) + return false; + break; + case SPELLFAMILY_PALADIN: + if( spellInfo_2->SpellFamilyName == SPELLFAMILY_PALADIN ) + { + // Paladin Seals + if( IsSealSpell(spellInfo_1) && IsSealSpell(spellInfo_2) ) + return true; + } + // Combustion and Fire Protection Aura (multi-family check) + if( spellInfo_2->Id == 11129 && spellInfo_1->SpellIconID == 33 && spellInfo_1->SpellVisual == 321 ) + return false; + + // *Sanctity Aura -> Unstable Currents and other (multi-family check) + if( spellInfo_1->SpellIconID==502 && spellInfo_2->SpellFamilyName == SPELLFAMILY_GENERIC && spellInfo_2->SpellIconID==502 && spellInfo_2->SpellVisual==969 ) + return false; + + // *Seal of Command and Band of Eternal Champion (multi-family check) + if( spellInfo_1->SpellIconID==561 && spellInfo_1->SpellVisual==7992 && spellId_2 == 35081) + return false; + break; + case SPELLFAMILY_SHAMAN: + if( spellInfo_2->SpellFamilyName == SPELLFAMILY_SHAMAN ) + { + // shaman shields + if( IsElementalShield(spellInfo_1) && IsElementalShield(spellInfo_2) ) + return true; + + // Windfury weapon + if( spellInfo_1->SpellIconID==220 && spellInfo_2->SpellIconID==220 && + spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags ) + return false; + } + // Bloodlust and Bloodthirst (multi-family check) + if( spellInfo_1->Id == 2825 && spellInfo_2->SpellIconID == 38 && spellInfo_2->SpellVisual == 0 ) + return false; + break; + default: + break; + } + + // more generic checks + if (spellInfo_1->SpellIconID == spellInfo_2->SpellIconID && + spellInfo_1->SpellIconID != 0 && spellInfo_2->SpellIconID != 0) + { + bool isModifier = false; + for (int i = 0; i < 3; i++) + { + if (spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_ADD_FLAT_MODIFIER || + spellInfo_1->EffectApplyAuraName[i] == SPELL_AURA_ADD_PCT_MODIFIER || + spellInfo_2->EffectApplyAuraName[i] == SPELL_AURA_ADD_FLAT_MODIFIER || + spellInfo_2->EffectApplyAuraName[i] == SPELL_AURA_ADD_PCT_MODIFIER ) + isModifier = true; + } + + if (!isModifier) + return true; + } + + if (IsRankSpellDueToSpell(spellInfo_1, spellId_2)) + return true; + + if (spellInfo_1->SpellFamilyName == 0 || spellInfo_2->SpellFamilyName == 0) + return false; + + if (spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName) + return false; + + for (int i = 0; i < 3; ++i) + if (spellInfo_1->Effect[i] != spellInfo_2->Effect[i] || + spellInfo_1->EffectItemType[i] != spellInfo_2->EffectItemType[i] || + spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i] || + spellInfo_1->EffectApplyAuraName[i] != spellInfo_2->EffectApplyAuraName[i]) + return false; + + return true; +} + +bool SpellMgr::IsProfessionSpell(uint32 spellId) +{ + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); + if(!spellInfo) + return false; + + if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL) + return false; + + uint32 skill = spellInfo->EffectMiscValue[1]; + + return IsProfessionSkill(skill); +} + +bool SpellMgr::IsPrimaryProfessionSpell(uint32 spellId) +{ + SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId); + if(!spellInfo) + return false; + + if(spellInfo->Effect[1] != SPELL_EFFECT_SKILL) + return false; + + uint32 skill = spellInfo->EffectMiscValue[1]; + + return IsPrimaryProfessionSkill(skill); +} + +bool SpellMgr::IsPrimaryProfessionFirstRankSpell(uint32 spellId) const +{ + return IsPrimaryProfessionSpell(spellId) && GetSpellRank(spellId)==1; +} + +SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const +{ + // ignore passive spells + if(IsPassiveSpell(spellInfo->Id)) + return spellInfo; + + bool needRankSelection = false; + for(int i=0;i<3;i++) + { + if( IsPositiveEffect(spellInfo->Id, i) && ( + spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA || + spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PARTY + ) ) + { + needRankSelection = true; + break; + } + } + + // not required + if(!needRankSelection) + return spellInfo; + + for(uint32 nextSpellId = spellInfo->Id; nextSpellId != 0; nextSpellId = GetPrevSpellInChain(nextSpellId)) + { + SpellEntry const *nextSpellInfo = sSpellStore.LookupEntry(nextSpellId); + if(!nextSpellInfo) + break; + + // if found appropriate level + if(playerLevel + 10 >= nextSpellInfo->spellLevel) + return nextSpellInfo; + + // one rank less then + } + + // not found + return NULL; +} + +void SpellMgr::LoadSpellChains() +{ + mSpellChains.clear(); // need for reload case + mSpellChainsNext.clear(); // need for reload case + + QueryResult *result = WorldDatabase.PQuery("SELECT spell_id, prev_spell, first_spell, rank, req_spell FROM spell_chain"); + if(result == NULL) + { + barGoLink bar( 1 ); + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded 0 spell chain records" ); + sLog.outErrorDb("`spell_chains` table is empty!"); + return; + } + + uint32 count = 0; + + barGoLink bar( result->GetRowCount() ); + do + { + bar.step(); + Field *fields = result->Fetch(); + + uint32 spell_id = fields[0].GetUInt32(); + + SpellChainNode node; + node.prev = fields[1].GetUInt32(); + node.first = fields[2].GetUInt32(); + node.rank = fields[3].GetUInt8(); + node.req = fields[4].GetUInt32(); + + if(!sSpellStore.LookupEntry(spell_id)) + { + sLog.outErrorDb("Spell %u listed in `spell_chain` does not exist",spell_id); + continue; + } + + if(node.prev!=0 && !sSpellStore.LookupEntry(node.prev)) + { + sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not existed previous rank spell.", + spell_id,node.prev,node.first,node.rank,node.req); + continue; + } + + if(!sSpellStore.LookupEntry(node.first)) + { + sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not existing first rank spell.", + spell_id,node.prev,node.first,node.rank,node.req); + continue; + } + + // check basic spell chain data integrity (note: rank can be equal 0 or 1 for first/single spell) + if( (spell_id == node.first) != (node.rank <= 1) || + (spell_id == node.first) != (node.prev == 0) || + (node.rank <= 1) != (node.prev == 0) ) + { + sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not compatible chain data.", + spell_id,node.prev,node.first,node.rank,node.req); + continue; + } + + if(node.req!=0 && !sSpellStore.LookupEntry(node.req)) + { + sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not existing required spell.", + spell_id,node.prev,node.first,node.rank,node.req); + continue; + } + + // talents not required data in spell chain for work, but must be checked if present for intergrity + if(TalentSpellPos const* pos = GetTalentSpellPos(spell_id)) + { + if(node.rank!=pos->rank+1) + { + sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong rank.", + spell_id,node.prev,node.first,node.rank,node.req); + continue; + } + + if(TalentEntry const* talentEntry = sTalentStore.LookupEntry(pos->talent_id)) + { + if(node.first!=talentEntry->RankID[0]) + { + sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong first rank spell.", + spell_id,node.prev,node.first,node.rank,node.req); + continue; + } + + if(node.rank > 1 && node.prev != talentEntry->RankID[node.rank-1-1]) + { + sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong prev rank spell.", + spell_id,node.prev,node.first,node.rank,node.req); + continue; + } + + if(node.req!=talentEntry->DependsOnSpell) + { + sLog.outErrorDb("Talent %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has wrong required spell.", + spell_id,node.prev,node.first,node.rank,node.req); + continue; + } + } + } + + mSpellChains[spell_id] = node; + + if(node.prev) + mSpellChainsNext.insert(SpellChainMapNext::value_type(node.prev,spell_id)); + + if(node.req) + mSpellChainsNext.insert(SpellChainMapNext::value_type(node.req,spell_id)); + + ++count; + } while( result->NextRow() ); + + // additional integrity checks + for(SpellChainMap::iterator i = mSpellChains.begin(); i != mSpellChains.end(); ++i) + { + if(i->second.prev) + { + SpellChainMap::iterator i_prev = mSpellChains.find(i->second.prev); + if(i_prev == mSpellChains.end()) + { + sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not found previous rank spell in table.", + i->first,i->second.prev,i->second.first,i->second.rank,i->second.req); + } + else if( i_prev->second.first != i->second.first ) + { + sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has different first spell in chain compared to previous rank spell (prev: %u, first: %u, rank: %d, req: %u).", + i->first,i->second.prev,i->second.first,i->second.rank,i->second.req, + i_prev->second.prev,i_prev->second.first,i_prev->second.rank,i_prev->second.req); + } + else if( i_prev->second.rank+1 != i->second.rank ) + { + sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has different rank compared to previous rank spell (prev: %u, first: %u, rank: %d, req: %u).", + i->first,i->second.prev,i->second.first,i->second.rank,i->second.req, + i_prev->second.prev,i_prev->second.first,i_prev->second.rank,i_prev->second.req); + } + } + + if(i->second.req) + { + SpellChainMap::iterator i_req = mSpellChains.find(i->second.req); + if(i_req == mSpellChains.end()) + { + sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has not found required rank spell in table.", + i->first,i->second.prev,i->second.first,i->second.rank,i->second.req); + } + else if( i_req->second.first == i->second.first ) + { + sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has required rank spell from same spell chain (prev: %u, first: %u, rank: %d, req: %u).", + i->first,i->second.prev,i->second.first,i->second.rank,i->second.req, + i_req->second.prev,i_req->second.first,i_req->second.rank,i_req->second.req); + } + else if( i_req->second.req ) + { + sLog.outErrorDb("Spell %u (prev: %u, first: %u, rank: %d, req: %u) listed in `spell_chain` has required rank spell with required spell (prev: %u, first: %u, rank: %d, req: %u).", + i->first,i->second.prev,i->second.first,i->second.rank,i->second.req, + i_req->second.prev,i_req->second.first,i_req->second.rank,i_req->second.req); + } + } + } + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u spell chain records", count ); +} + +void SpellMgr::LoadSpellLearnSkills() +{ + mSpellLearnSkills.clear(); // need for reload case + + // search auto-learned skills and add its to map also for use in unlearn spells/talents + uint32 dbc_count = 0; + for(uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell) + { + SpellEntry const* entry = sSpellStore.LookupEntry(spell); + + if(!entry) + continue; + + for(int i = 0; i < 3; ++i) + { + if(entry->Effect[i]==SPELL_EFFECT_SKILL) + { + SpellLearnSkillNode dbc_node; + dbc_node.skill = entry->EffectMiscValue[i]; + if ( dbc_node.skill != SKILL_RIDING ) + dbc_node.value = 1; + else + dbc_node.value = (entry->EffectBasePoints[i]+1)*75; + dbc_node.maxvalue = (entry->EffectBasePoints[i]+1)*75; + + SpellLearnSkillNode const* db_node = GetSpellLearnSkill(spell); + + mSpellLearnSkills[spell] = dbc_node; + ++dbc_count; + break; + } + } + } + + sLog.outString(); + sLog.outString( ">> Loaded %u Spell Learn Skills from DBC", dbc_count ); +} + +void SpellMgr::LoadSpellLearnSpells() +{ + mSpellLearnSpells.clear(); // need for reload case + + QueryResult *result = WorldDatabase.PQuery("SELECT entry, SpellID FROM spell_learn_spell"); + if(!result) + { + barGoLink bar( 1 ); + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded 0 spell learn spells" ); + sLog.outErrorDb("`spell_learn_spell` table is empty!"); + return; + } + + uint32 count = 0; + + barGoLink bar( result->GetRowCount() ); + do + { + bar.step(); + Field *fields = result->Fetch(); + + uint32 spell_id = fields[0].GetUInt32(); + + SpellLearnSpellNode node; + node.spell = fields[1].GetUInt32(); + node.autoLearned= false; + + if(!sSpellStore.LookupEntry(spell_id)) + { + sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",spell_id); + continue; + } + + if(!sSpellStore.LookupEntry(node.spell)) + { + sLog.outErrorDb("Spell %u listed in `spell_learn_spell` does not exist",node.spell); + continue; + } + + mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell_id,node)); + + ++count; + } while( result->NextRow() ); + + delete result; + + // search auto-learned spells and add its to map also for use in unlearn spells/talents + uint32 dbc_count = 0; + for(uint32 spell = 0; spell < sSpellStore.GetNumRows(); ++spell) + { + SpellEntry const* entry = sSpellStore.LookupEntry(spell); + + if(!entry) + continue; + + for(int i = 0; i < 3; ++i) + { + if(entry->Effect[i]==SPELL_EFFECT_LEARN_SPELL) + { + SpellLearnSpellNode dbc_node; + dbc_node.spell = entry->EffectTriggerSpell[i]; + dbc_node.autoLearned = true; + + SpellLearnSpellMap::const_iterator db_node_begin = GetBeginSpellLearnSpell(spell); + SpellLearnSpellMap::const_iterator db_node_end = GetEndSpellLearnSpell(spell); + + bool found = false; + for(SpellLearnSpellMap::const_iterator itr = db_node_begin; itr != db_node_end; ++itr) + { + if(itr->second.spell == dbc_node.spell) + { + sLog.outErrorDb("Spell %u auto-learn spell %u in spell.dbc then the record in `spell_learn_spell` is redundant, please fix DB.", + spell,dbc_node.spell); + found = true; + break; + } + } + + if(!found) // add new spell-spell pair if not found + { + mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell,dbc_node)); + ++dbc_count; + } + } + } + } + + sLog.outString(); + sLog.outString( ">> Loaded %u spell learn spells + %u found in DBC", count, dbc_count ); +} + +void SpellMgr::LoadSpellScriptTarget() +{ + mSpellScriptTarget.clear(); // need for reload case + + uint32 count = 0; + + QueryResult *result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM spell_script_target"); + + if(!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded 0 SpellScriptTarget. DB table `spell_script_target` is empty."); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + Field *fields = result->Fetch(); + bar.step(); + + uint32 spellId = fields[0].GetUInt32(); + uint32 type = fields[1].GetUInt32(); + uint32 targetEntry = fields[2].GetUInt32(); + + SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId); + + if(!spellProto) + { + sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not exist.",spellId,targetEntry); + continue; + } + + bool targetfound = false; + for(int i = 0; i <3; ++i) + { + if( spellProto->EffectImplicitTargetA[i]==TARGET_SCRIPT || + spellProto->EffectImplicitTargetB[i]==TARGET_SCRIPT || + spellProto->EffectImplicitTargetA[i]==TARGET_SCRIPT_COORDINATES || + spellProto->EffectImplicitTargetB[i]==TARGET_SCRIPT_COORDINATES ) + { + targetfound = true; + break; + } + } + if(!targetfound) + { + sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not have any implicit target TARGET_SCRIPT(38) or TARGET_SCRIPT_COORDINATES (46).",spellId,targetEntry); + continue; + } + + if( type >= MAX_SPELL_TARGET_TYPE ) + { + sLog.outErrorDb("Table `spell_script_target`: target type %u for TargetEntry %u is incorrect.",type,targetEntry); + continue; + } + + switch(type) + { + case SPELL_TARGET_TYPE_GAMEOBJECT: + { + if( targetEntry==0 ) + break; + + if(!sGOStorage.LookupEntry(targetEntry)) + { + sLog.outErrorDb("Table `spell_script_target`: gameobject template entry %u does not exist.",targetEntry); + continue; + } + break; + } + default: + { + if( targetEntry==0 ) + { + sLog.outErrorDb("Table `spell_script_target`: target entry == 0 for not GO target type (%u).",type); + continue; + } + if(!sCreatureStorage.LookupEntry(targetEntry)) + { + sLog.outErrorDb("Table `spell_script_target`: creature template entry %u does not exist.",targetEntry); + continue; + } + const CreatureInfo* cInfo = sCreatureStorage.LookupEntry(targetEntry); + + if(spellId == 30427 && !cInfo->SkinLootId) + { + sLog.outErrorDb("Table `spell_script_target` has creature %u as a target of spellid 30427, but this creature has no skinlootid. Gas extraction will not work!", cInfo->Entry); + continue; + } + break; + } + } + + mSpellScriptTarget.insert(SpellScriptTarget::value_type(spellId,SpellTargetEntry(SpellTargetType(type),targetEntry))); + + ++count; + } while (result->NextRow()); + + delete result; + + // Check all spells + /* Disabled (lot errors at this moment) + for(uint32 i = 1; i < sSpellStore.nCount; ++i) + { + SpellEntry const * spellInfo = sSpellStore.LookupEntry(i); + if(!spellInfo) + continue; + + bool found = false; + for(int j=0; j<3; ++j) + { + if( spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT || spellInfo->EffectImplicitTargetA[j] != TARGET_SELF && spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT ) + { + SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(spellInfo->Id); + SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(spellInfo->Id); + if(lower==upper) + { + sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = %u (TARGET_SCRIPT), but does not have record in `spell_script_target`",spellInfo->Id,TARGET_SCRIPT); + break; // effects of spell + } + } + } + } + */ + + sLog.outString(); + sLog.outString(">> Loaded %u Spell Script Targets", count); +} + +void SpellMgr::LoadSpellPetAuras() +{ + mSpellPetAuraMap.clear(); // need for reload case + + uint32 count = 0; + + // 0 1 2 + QueryResult *result = WorldDatabase.Query("SELECT spell, pet, aura FROM spell_pet_auras"); + if( !result ) + { + + barGoLink bar( 1 ); + + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u spell pet auras", count ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + Field *fields = result->Fetch(); + + bar.step(); + + uint16 spell = fields[0].GetUInt16(); + uint16 pet = fields[1].GetUInt16(); + uint16 aura = fields[2].GetUInt16(); + + SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find(spell); + if(itr != mSpellPetAuraMap.end()) + { + itr->second.AddAura(pet, aura); + } + else + { + SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); + if (!spellInfo) + { + sLog.outErrorDb("Spell %u listed in `spell_pet_auras` does not exist", spell); + continue; + } + int i = 0; + for(; i < 3; ++i) + if((spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA && + spellInfo->EffectApplyAuraName[i] == SPELL_AURA_DUMMY) || + spellInfo->Effect[i] == SPELL_EFFECT_DUMMY) + break; + + if(i == 3) + { + sLog.outError("Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell); + continue; + } + + SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(aura); + if (!spellInfo2) + { + sLog.outErrorDb("Aura %u listed in `spell_pet_auras` does not exist", aura); + continue; + } + + PetAura pa(pet, aura, spellInfo->EffectImplicitTargetA[i] == TARGET_PET, spellInfo->EffectBasePoints[i] + spellInfo->EffectBaseDice[i]); + mSpellPetAuraMap[spell] = pa; + } + + ++count; + } while( result->NextRow() ); + + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u spell pet auras", count ); +} + +/// Some checks for spells, to prevent adding depricated/broken spells for trainers, spell book, etc +bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg) +{ + // not exist + if(!spellInfo) + return false; + + bool need_check_reagents = false; + + // check effects + for(int i=0; i<3; ++i) + { + switch(spellInfo->Effect[i]) + { + case 0: + continue; + + // craft spell for crafting non-existed item (break client recipes list show) + case SPELL_EFFECT_CREATE_ITEM: + { + if(!ObjectMgr::GetItemPrototype( spellInfo->EffectItemType[i] )) + { + if(msg) + { + if(pl) + ChatHandler(pl).PSendSysMessage("Craft spell %u create not-exist in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->EffectItemType[i]); + else + sLog.outErrorDb("Craft spell %u create not-exist in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->EffectItemType[i]); + } + return false; + } + + need_check_reagents = true; + break; + } + case SPELL_EFFECT_LEARN_SPELL: + { + SpellEntry const* spellInfo2 = sSpellStore.LookupEntry(spellInfo->EffectTriggerSpell[0]); + if( !IsSpellValid(spellInfo2,pl,msg) ) + { + if(msg) + { + if(pl) + ChatHandler(pl).PSendSysMessage("Spell %u learn to broken spell %u, and then...",spellInfo->Id,spellInfo->EffectTriggerSpell[0]); + else + sLog.outErrorDb("Spell %u learn to invalid spell %u, and then...",spellInfo->Id,spellInfo->EffectTriggerSpell[0]); + } + return false; + } + break; + } + } + } + + if(need_check_reagents) + { + for(int j = 0; j < 8; ++j) + { + if(spellInfo->Reagent[j] > 0 && !ObjectMgr::GetItemPrototype( spellInfo->Reagent[j] )) + { + if(msg) + { + if(pl) + ChatHandler(pl).PSendSysMessage("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->Reagent[j]); + else + sLog.outErrorDb("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...",spellInfo->Id,spellInfo->Reagent[j]); + } + return false; + } + } + } + + return true; +} + +bool IsSpellAllowedInLocation(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id) +{ + // normal case + if( spellInfo->AreaId && spellInfo->AreaId != zone_id && spellInfo->AreaId != area_id ) + return false; + + // elixirs (all area dependent elixirs have family SPELLFAMILY_POTION, use this for speedup) + if(spellInfo->SpellFamilyName==SPELLFAMILY_POTION) + { + if(uint32 mask = spellmgr.GetSpellElixirMask(spellInfo->Id)) + { + if(mask & ELIXIR_UNSTABLE_MASK) + { + // in the Blade's Edge Mountains Plateaus and Gruul's Lair. + return zone_id ==3522 || map_id==565; + } + if(mask & ELIXIR_UNSTABLE_MASK) + { + // in Tempest Keep, Serpentshrine Cavern, Caverns of Time: Mount Hyjal, Black Temple + // TODO: and the Sunwell Plateau + if(zone_id ==3607 || map_id==534 || map_id==564) + return true; + + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); + if(!mapEntry) + return false; + + return mapEntry->multimap_id==206; + } + + // elixirs not have another limitations + return true; + } + } + + // special cases zone check (maps checked by multimap common id) + switch(spellInfo->Id) + { + case 41618: + case 41620: + { + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); + if(!mapEntry) + return false; + + return mapEntry->multimap_id==206; + } + + case 41617: + case 41619: + { + MapEntry const* mapEntry = sMapStore.LookupEntry(map_id); + if(!mapEntry) + return false; + + return mapEntry->multimap_id==207; + } + // Dragonmaw Illusion + case 40216: + case 42016: + { + if ( area_id != 3759 && area_id != 3966 && area_id != 3939 ) + return false; + break; + } + } + + return true; +} + +void SpellMgr::LoadSkillLineAbilityMap() +{ + mSkillLineAbilityMap.clear(); + + uint32 count = 0; + + for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); i++) + { + SkillLineAbilityEntry const *SkillInfo = sSkillLineAbilityStore.LookupEntry(i); + if(!SkillInfo) + continue; + + mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->spellId,SkillInfo)); + ++count; + } + + sLog.outString(); + sLog.outString(">> Loaded %u SkillLineAbility MultiMap", count); +} + +DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered) +{ + // Explicit Diminishing Groups + switch(spellproto->SpellFamilyName) + { + case SPELLFAMILY_MAGE: + { + // Polymorph + if ((spellproto->SpellFamilyFlags & 0x00001000000LL) && spellproto->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE) + return DIMINISHING_POLYMORPH; + break; + } + case SPELLFAMILY_ROGUE: + { + // Kidney Shot + if (spellproto->SpellFamilyFlags & 0x00000200000LL) + return DIMINISHING_KIDNEYSHOT; + // Blind + else if (spellproto->SpellFamilyFlags & 0x00001000000LL) + return DIMINISHING_BLIND_CYCLONE; + break; + } + case SPELLFAMILY_WARLOCK: + { + // Death Coil + if (spellproto->SpellFamilyFlags & 0x00000080000LL) + return DIMINISHING_DEATHCOIL; + // Fear + else if (spellproto->SpellFamilyFlags & 0x40840000000LL) + return DIMINISHING_WARLOCK_FEAR; + // Curses/etc + else if (spellproto->SpellFamilyFlags & 0x00080000000LL) + return DIMINISHING_LIMITONLY; + break; + } + case SPELLFAMILY_DRUID: + { + // Cyclone + if (spellproto->SpellFamilyFlags & 0x02000000000LL) + return DIMINISHING_BLIND_CYCLONE; + break; + } + case SPELLFAMILY_WARRIOR: + { + // Hamstring - limit duration to 10s in PvP + if (spellproto->SpellFamilyFlags & 0x00000000002LL) + return DIMINISHING_LIMITONLY; + break; + } + default: + break; + } + + // Get by mechanic + for (uint8 i=0;i<3;++i) + { + if (spellproto->Mechanic == MECHANIC_STUN || spellproto->EffectMechanic[i] == MECHANIC_STUN) + return triggered ? DIMINISHING_TRIGGER_STUN : DIMINISHING_CONTROL_STUN; + else if (spellproto->Mechanic == MECHANIC_SLEEP || spellproto->EffectMechanic[i] == MECHANIC_SLEEP) + return DIMINISHING_SLEEP; + else if (spellproto->Mechanic == MECHANIC_ROOT || spellproto->EffectMechanic[i] == MECHANIC_ROOT) + return triggered ? DIMINISHING_TRIGGER_ROOT : DIMINISHING_CONTROL_ROOT; + else if (spellproto->Mechanic == MECHANIC_FEAR || spellproto->EffectMechanic[i] == MECHANIC_FEAR) + return DIMINISHING_FEAR; + else if (spellproto->Mechanic == MECHANIC_CHARM || spellproto->EffectMechanic[i] == MECHANIC_CHARM) + return DIMINISHING_CHARM; + else if (spellproto->Mechanic == MECHANIC_SILENCE || spellproto->EffectMechanic[i] == MECHANIC_SILENCE) + return DIMINISHING_SILENCE; + else if (spellproto->Mechanic == MECHANIC_DISARM || spellproto->EffectMechanic[i] == MECHANIC_DISARM) + return DIMINISHING_DISARM; + else if (spellproto->Mechanic == MECHANIC_FREEZE || spellproto->EffectMechanic[i] == MECHANIC_FREEZE) + return DIMINISHING_FREEZE; + else if (spellproto->Mechanic == MECHANIC_KNOCKOUT|| spellproto->EffectMechanic[i] == MECHANIC_KNOCKOUT || + spellproto->Mechanic == MECHANIC_SAPPED || spellproto->EffectMechanic[i] == MECHANIC_SAPPED) + return DIMINISHING_KNOCKOUT; + else if (spellproto->Mechanic == MECHANIC_BANISH || spellproto->EffectMechanic[i] == MECHANIC_BANISH) + return DIMINISHING_BANISH; + } + + return DIMINISHING_NONE; +} + +bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group) +{ + switch(group) + { + case DIMINISHING_CONTROL_STUN: + case DIMINISHING_TRIGGER_STUN: + case DIMINISHING_KIDNEYSHOT: + case DIMINISHING_SLEEP: + case DIMINISHING_CONTROL_ROOT: + case DIMINISHING_TRIGGER_ROOT: + case DIMINISHING_FEAR: + case DIMINISHING_WARLOCK_FEAR: + case DIMINISHING_CHARM: + case DIMINISHING_POLYMORPH: + case DIMINISHING_FREEZE: + case DIMINISHING_KNOCKOUT: + case DIMINISHING_BLIND_CYCLONE: + case DIMINISHING_BANISH: + case DIMINISHING_LIMITONLY: + return true; + } + return false; +} + +DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group) +{ + switch(group) + { + case DIMINISHING_BLIND_CYCLONE: + case DIMINISHING_CONTROL_STUN: + case DIMINISHING_TRIGGER_STUN: + case DIMINISHING_KIDNEYSHOT: + return DRTYPE_ALL; + case DIMINISHING_SLEEP: + case DIMINISHING_CONTROL_ROOT: + case DIMINISHING_TRIGGER_ROOT: + case DIMINISHING_FEAR: + case DIMINISHING_CHARM: + case DIMINISHING_POLYMORPH: + case DIMINISHING_SILENCE: + case DIMINISHING_DISARM: + case DIMINISHING_DEATHCOIL: + case DIMINISHING_FREEZE: + case DIMINISHING_BANISH: + case DIMINISHING_WARLOCK_FEAR: + case DIMINISHING_KNOCKOUT: + return DRTYPE_PLAYER; + } + + return DRTYPE_NONE; +} diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h new file mode 100644 index 00000000000..7e8ffc82564 --- /dev/null +++ b/src/game/SpellMgr.h @@ -0,0 +1,859 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _SPELLMGR_H +#define _SPELLMGR_H + +// For static or at-server-startup loaded spell data +// For more high level function for sSpellStore data + +#include "SharedDefines.h" +#include "Database/DBCStructure.h" +#include "Database/SQLStorage.h" + +#include "Utilities/HashMap.h" +#include + +class Player; +class Spell; + +extern SQLStorage sSpellThreatStore; + +enum SpellFailedReason +{ + SPELL_FAILED_AFFECTING_COMBAT = 0x00, + SPELL_FAILED_ALREADY_AT_FULL_HEALTH = 0x01, + SPELL_FAILED_ALREADY_AT_FULL_MANA = 0x02, + SPELL_FAILED_ALREADY_AT_FULL_POWER = 0x03, + SPELL_FAILED_ALREADY_BEING_TAMED = 0x04, + SPELL_FAILED_ALREADY_HAVE_CHARM = 0x05, + SPELL_FAILED_ALREADY_HAVE_SUMMON = 0x06, + SPELL_FAILED_ALREADY_OPEN = 0x07, + SPELL_FAILED_AURA_BOUNCED = 0x08, + SPELL_FAILED_AUTOTRACK_INTERRUPTED = 0x09, + SPELL_FAILED_BAD_IMPLICIT_TARGETS = 0x0A, + SPELL_FAILED_BAD_TARGETS = 0x0B, + SPELL_FAILED_CANT_BE_CHARMED = 0x0C, + SPELL_FAILED_CANT_BE_DISENCHANTED = 0x0D, + SPELL_FAILED_CANT_BE_DISENCHANTED_SKILL = 0x0E, + SPELL_FAILED_CANT_BE_PROSPECTED = 0x0F, + SPELL_FAILED_CANT_CAST_ON_TAPPED = 0x10, + SPELL_FAILED_CANT_DUEL_WHILE_INVISIBLE = 0x11, + SPELL_FAILED_CANT_DUEL_WHILE_STEALTHED = 0x12, + SPELL_FAILED_CANT_STEALTH = 0x13, + SPELL_FAILED_CASTER_AURASTATE = 0x14, + SPELL_FAILED_CASTER_DEAD = 0x15, + SPELL_FAILED_CHARMED = 0x16, + SPELL_FAILED_CHEST_IN_USE = 0x17, + SPELL_FAILED_CONFUSED = 0x18, + SPELL_FAILED_DONT_REPORT = 0x19, + SPELL_FAILED_EQUIPPED_ITEM = 0x1A, + SPELL_FAILED_EQUIPPED_ITEM_CLASS = 0x1B, + SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND = 0x1C, + SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND = 0x1D, + SPELL_FAILED_ERROR = 0x1E, + SPELL_FAILED_FIZZLE = 0x1F, + SPELL_FAILED_FLEEING = 0x20, + SPELL_FAILED_FOOD_LOWLEVEL = 0x21, + SPELL_FAILED_HIGHLEVEL = 0x22, + SPELL_FAILED_HUNGER_SATIATED = 0x23, + SPELL_FAILED_IMMUNE = 0x24, + SPELL_FAILED_INTERRUPTED = 0x25, + SPELL_FAILED_INTERRUPTED_COMBAT = 0x26, + SPELL_FAILED_ITEM_ALREADY_ENCHANTED = 0x27, + SPELL_FAILED_ITEM_GONE = 0x28, + SPELL_FAILED_ITEM_NOT_FOUND = 0x29, + SPELL_FAILED_ITEM_NOT_READY = 0x2A, + SPELL_FAILED_LEVEL_REQUIREMENT = 0x2B, + SPELL_FAILED_LINE_OF_SIGHT = 0x2C, + SPELL_FAILED_LOWLEVEL = 0x2D, + SPELL_FAILED_LOW_CASTLEVEL = 0x2E, + SPELL_FAILED_MAINHAND_EMPTY = 0x2F, + SPELL_FAILED_MOVING = 0x30, + SPELL_FAILED_NEED_AMMO = 0x31, + SPELL_FAILED_NEED_AMMO_POUCH = 0x32, + SPELL_FAILED_NEED_EXOTIC_AMMO = 0x33, + SPELL_FAILED_NOPATH = 0x34, + SPELL_FAILED_NOT_BEHIND = 0x35, + SPELL_FAILED_NOT_FISHABLE = 0x36, + SPELL_FAILED_NOT_FLYING = 0x37, + SPELL_FAILED_NOT_HERE = 0x38, + SPELL_FAILED_NOT_INFRONT = 0x39, + SPELL_FAILED_NOT_IN_CONTROL = 0x3A, + SPELL_FAILED_NOT_KNOWN = 0x3B, + SPELL_FAILED_NOT_MOUNTED = 0x3C, + SPELL_FAILED_NOT_ON_TAXI = 0x3D, + SPELL_FAILED_NOT_ON_TRANSPORT = 0x3E, + SPELL_FAILED_NOT_READY = 0x3F, + SPELL_FAILED_NOT_SHAPESHIFT = 0x40, + SPELL_FAILED_NOT_STANDING = 0x41, + SPELL_FAILED_NOT_TRADEABLE = 0x42, + SPELL_FAILED_NOT_TRADING = 0x43, + SPELL_FAILED_NOT_UNSHEATHED = 0x44, + SPELL_FAILED_NOT_WHILE_GHOST = 0x45, + SPELL_FAILED_NO_AMMO = 0x46, + SPELL_FAILED_NO_CHARGES_REMAIN = 0x47, + SPELL_FAILED_NO_CHAMPION = 0x48, + SPELL_FAILED_NO_COMBO_POINTS = 0x49, + SPELL_FAILED_NO_DUELING = 0x4A, + SPELL_FAILED_NO_ENDURANCE = 0x4B, + SPELL_FAILED_NO_FISH = 0x4C, + SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED = 0x4D, + SPELL_FAILED_NO_MOUNTS_ALLOWED = 0x4E, + SPELL_FAILED_NO_PET = 0x4F, + SPELL_FAILED_NO_POWER = 0x50, + SPELL_FAILED_NOTHING_TO_DISPEL = 0x51, + SPELL_FAILED_NOTHING_TO_STEAL = 0x52, + SPELL_FAILED_ONLY_ABOVEWATER = 0x53, + SPELL_FAILED_ONLY_DAYTIME = 0x54, + SPELL_FAILED_ONLY_INDOORS = 0x55, + SPELL_FAILED_ONLY_MOUNTED = 0x56, + SPELL_FAILED_ONLY_NIGHTTIME = 0x57, + SPELL_FAILED_ONLY_OUTDOORS = 0x58, + SPELL_FAILED_ONLY_SHAPESHIFT = 0x59, + SPELL_FAILED_ONLY_STEALTHED = 0x5A, + SPELL_FAILED_ONLY_UNDERWATER = 0x5B, + SPELL_FAILED_OUT_OF_RANGE = 0x5C, + SPELL_FAILED_PACIFIED = 0x5D, + SPELL_FAILED_POSSESSED = 0x5E, + SPELL_FAILED_REAGENTS = 0x5F, + SPELL_FAILED_REQUIRES_AREA = 0x60, + SPELL_FAILED_REQUIRES_SPELL_FOCUS = 0x61, + SPELL_FAILED_ROOTED = 0x62, + SPELL_FAILED_SILENCED = 0x63, + SPELL_FAILED_SPELL_IN_PROGRESS = 0x64, + SPELL_FAILED_SPELL_LEARNED = 0x65, + SPELL_FAILED_SPELL_UNAVAILABLE = 0x66, + SPELL_FAILED_STUNNED = 0x67, + SPELL_FAILED_TARGETS_DEAD = 0x68, + SPELL_FAILED_TARGET_AFFECTING_COMBAT = 0x69, + SPELL_FAILED_TARGET_AURASTATE = 0x6A, + SPELL_FAILED_TARGET_DUELING = 0x6B, + SPELL_FAILED_TARGET_ENEMY = 0x6C, + SPELL_FAILED_TARGET_ENRAGED = 0x6D, + SPELL_FAILED_TARGET_FRIENDLY = 0x6E, + SPELL_FAILED_TARGET_IN_COMBAT = 0x6F, + SPELL_FAILED_TARGET_IS_PLAYER = 0x70, + SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED = 0x71, + SPELL_FAILED_TARGET_NOT_DEAD = 0x72, + SPELL_FAILED_TARGET_NOT_IN_PARTY = 0x73, + SPELL_FAILED_TARGET_NOT_LOOTED = 0x74, + SPELL_FAILED_TARGET_NOT_PLAYER = 0x75, + SPELL_FAILED_TARGET_NO_POCKETS = 0x76, + SPELL_FAILED_TARGET_NO_WEAPONS = 0x77, + SPELL_FAILED_TARGET_UNSKINNABLE = 0x78, + SPELL_FAILED_THIRST_SATIATED = 0x79, + SPELL_FAILED_TOO_CLOSE = 0x7A, + SPELL_FAILED_TOO_MANY_OF_ITEM = 0x7B, + SPELL_FAILED_TOTEM_CATEGORY = 0x7C, + SPELL_FAILED_TOTEMS = 0x7D, + SPELL_FAILED_TRAINING_POINTS = 0x7E, + SPELL_FAILED_TRY_AGAIN = 0x7F, + SPELL_FAILED_UNIT_NOT_BEHIND = 0x80, + SPELL_FAILED_UNIT_NOT_INFRONT = 0x81, + SPELL_FAILED_WRONG_PET_FOOD = 0x82, + SPELL_FAILED_NOT_WHILE_FATIGUED = 0x83, + SPELL_FAILED_TARGET_NOT_IN_INSTANCE = 0x84, + SPELL_FAILED_NOT_WHILE_TRADING = 0x85, + SPELL_FAILED_TARGET_NOT_IN_RAID = 0x86, + SPELL_FAILED_DISENCHANT_WHILE_LOOTING = 0x87, + SPELL_FAILED_PROSPECT_WHILE_LOOTING = 0x88, + SPELL_FAILED_PROSPECT_NEED_MORE = 0x89, + SPELL_FAILED_TARGET_FREEFORALL = 0x8A, + SPELL_FAILED_NO_EDIBLE_CORPSES = 0x8B, + SPELL_FAILED_ONLY_BATTLEGROUNDS = 0x8C, + SPELL_FAILED_TARGET_NOT_GHOST = 0x8D, + SPELL_FAILED_TOO_MANY_SKILLS = 0x8E, + SPELL_FAILED_TRANSFORM_UNUSABLE = 0x8F, + SPELL_FAILED_WRONG_WEATHER = 0x90, + SPELL_FAILED_DAMAGE_IMMUNE = 0x91, + SPELL_FAILED_PREVENTED_BY_MECHANIC = 0x92, + SPELL_FAILED_PLAY_TIME = 0x93, + SPELL_FAILED_REPUTATION = 0x94, + SPELL_FAILED_MIN_SKILL = 0x95, + SPELL_FAILED_NOT_IN_ARENA = 0x96, + SPELL_FAILED_NOT_ON_SHAPESHIFT = 0x97, + SPELL_FAILED_NOT_ON_STEALTHED = 0x98, + SPELL_FAILED_NOT_ON_DAMAGE_IMMUNE = 0x99, + SPELL_FAILED_NOT_ON_MOUNTED = 0x9A, + SPELL_FAILED_TOO_SHALLOW = 0x9B, + SPELL_FAILED_TARGET_NOT_IN_SANCTUARY = 0x9C, + SPELL_FAILED_TARGET_IS_TRIVIAL = 0x9D, + SPELL_FAILED_BM_OR_INVISGOD = 0x9E, + SPELL_FAILED_EXPERT_RIDING_REQUIREMENT = 0x9F, + SPELL_FAILED_ARTISAN_RIDING_REQUIREMENT = 0xA0, + SPELL_FAILED_NOT_IDLE = 0xA1, + SPELL_FAILED_NOT_INACTIVE = 0xA2, + SPELL_FAILED_PARTIAL_PLAYTIME = 0xA3, + SPELL_FAILED_NO_PLAYTIME = 0xA4, + SPELL_FAILED_NOT_IN_BATTLEGROUND = 0xA5, + SPELL_FAILED_ONLY_IN_ARENA = 0xA6, + SPELL_FAILED_TARGET_LOCKED_TO_RAID_INSTANCE = 0xA7, + SPELL_FAILED_UNKNOWN = 0xA8, +}; + +enum SpellFamilyNames +{ + SPELLFAMILY_GENERIC = 0, + SPELLFAMILY_UNK1 = 1, // events, holidays + // 2 - unused + SPELLFAMILY_MAGE = 3, + SPELLFAMILY_WARRIOR = 4, + SPELLFAMILY_WARLOCK = 5, + SPELLFAMILY_PRIEST = 6, + SPELLFAMILY_DRUID = 7, + SPELLFAMILY_ROGUE = 8, + SPELLFAMILY_HUNTER = 9, + SPELLFAMILY_PALADIN = 10, + SPELLFAMILY_SHAMAN = 11, + SPELLFAMILY_UNK2 = 12, + SPELLFAMILY_POTION = 13 +}; + +//Some SpellFamilyFlags +#define SPELLFAMILYFLAG_ROGUE_VANISH 0x000000800LL +#define SPELLFAMILYFLAG_ROGUE_STEALTH 0x000400000LL +#define SPELLFAMILYFLAG_ROGUE_BACKSTAB 0x000800004LL +#define SPELLFAMILYFLAG_ROGUE_SAP 0x000000080LL +#define SPELLFAMILYFLAG_ROGUE_FEINT 0x008000000LL +#define SPELLFAMILYFLAG_ROGUE_KIDNEYSHOT 0x000200000LL +#define SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE 0x9003E0000LL + +// Spell clasification +enum SpellSpecific +{ + SPELL_NORMAL = 0, + SPELL_SEAL = 1, + SPELL_BLESSING = 2, + SPELL_AURA = 3, + SPELL_STING = 4, + SPELL_CURSE = 5, + SPELL_ASPECT = 6, + SPELL_TRACKER = 7, + SPELL_WARLOCK_ARMOR = 8, + SPELL_MAGE_ARMOR = 9, + SPELL_ELEMENTAL_SHIELD = 10, + SPELL_MAGE_POLYMORPH = 11, + SPELL_POSITIVE_SHOUT = 12, + SPELL_JUDGEMENT = 13, + SPELL_BATTLE_ELIXIR = 14, + SPELL_GUARDIAN_ELIXIR = 15, + SPELL_FLASK_ELIXIR = 16 +}; + +SpellSpecific GetSpellSpecific(uint32 spellId); + +// Different spell properties +inline float GetSpellRadius(SpellRadiusEntry const *radius) { return (radius ? radius->Radius : 0); } +uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell const* spell = NULL); +inline float GetSpellMinRange(SpellRangeEntry const *range) { return (range ? range->minRange : 0); } +inline float GetSpellMaxRange(SpellRangeEntry const *range) { return (range ? range->maxRange : 0); } +inline uint32 GetSpellRecoveryTime(SpellEntry const *spellInfo) { return spellInfo->RecoveryTime > spellInfo->CategoryRecoveryTime ? spellInfo->RecoveryTime : spellInfo->CategoryRecoveryTime; } +int32 GetSpellDuration(SpellEntry const *spellInfo); +int32 GetSpellMaxDuration(SpellEntry const *spellInfo); + +inline bool IsSpellHaveEffect(SpellEntry const *spellInfo, SpellEffects effect) +{ + for(int i= 0; i < 3; ++i) + if(spellInfo->Effect[i]==effect) + return true; + return false; +} + +bool IsNoStackAuraDueToAura(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2); + +inline bool IsSealSpell(SpellEntry const *spellInfo) +{ + //Collection of all the seal family flags. No other paladin spell has any of those. + return spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && + ( spellInfo->SpellFamilyFlags & 0x4000A000200LL ); +} + +inline bool IsElementalShield(SpellEntry const *spellInfo) +{ + // family flags 10 (Lightning), 42 (Earth), 37 (Water), proc shield from T2 8 pieces bonus + return (spellInfo->SpellFamilyFlags & 0x42000000400LL) || spellInfo->Id == 23552; +} + +int32 CompareAuraRanks(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2); +bool IsSingleFromSpellSpecificPerCaster(uint32 spellSpec1,uint32 spellSpec2); +bool IsPassiveSpell(uint32 spellId); + +inline bool IsDeathPersistentSpell(SpellEntry const *spellInfo) +{ + switch(spellInfo->Id) + { + case 40214: // Dragonmaw Illusion + case 35480: case 35481: case 35482: // Human Illusion + case 35483: case 39824: // Human Illusion + return true; + } + + return spellInfo->AttributesEx3 & SPELL_ATTR_EX3_DEATH_PERSISTENT; +} + +inline bool IsNonCombatSpell(SpellEntry const *spellInfo) +{ + return (spellInfo->Attributes & SPELL_ATTR_CANT_USED_IN_COMBAT) != 0; +} + +bool IsPositiveSpell(uint32 spellId); +bool IsPositiveEffect(uint32 spellId, uint32 effIndex); +bool IsPositiveTarget(uint32 targetA, uint32 targetB); + +bool IsSingleTargetSpell(SpellEntry const *spellInfo); +bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2); + +bool IsSpellAllowedInLocation(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id); + +inline bool IsAreaEffectTarget( Targets target ) +{ + switch (target ) + { + case TARGET_AREAEFFECT_CUSTOM: + case TARGET_ALL_ENEMY_IN_AREA: + case TARGET_ALL_ENEMY_IN_AREA_INSTANT: + case TARGET_ALL_PARTY_AROUND_CASTER: + case TARGET_ALL_AROUND_CASTER: + case TARGET_ALL_ENEMY_IN_AREA_CHANNELED: + case TARGET_ALL_FRIENDLY_UNITS_AROUND_CASTER: + case TARGET_ALL_PARTY: + case TARGET_ALL_PARTY_AROUND_CASTER_2: + case TARGET_AREAEFFECT_PARTY: + case TARGET_AREAEFFECT_CUSTOM_2: + case TARGET_AREAEFFECT_PARTY_AND_CLASS: + case TARGET_IN_FRONT_OF_CASTER: + case TARGET_ALL_FRIENDLY_UNITS_IN_AREA: + return true; + default: + break; + } + return false; +} + +inline bool IsAreaOfEffectSpell(SpellEntry const *spellInfo) +{ + if(IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetA[0])) || IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetB[0]))) + return true; + if(IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetA[1])) || IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetB[1]))) + return true; + if(IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetA[2])) || IsAreaEffectTarget(Targets(spellInfo->EffectImplicitTargetB[2]))) + return true; + return false; +} + +inline bool IsAreaAuraEffect(uint32 effect) +{ + if( effect == SPELL_EFFECT_APPLY_AREA_AURA_PARTY || + effect == SPELL_EFFECT_APPLY_AREA_AURA_FRIEND || + effect == SPELL_EFFECT_APPLY_AREA_AURA_ENEMY || + effect == SPELL_EFFECT_APPLY_AREA_AURA_PET || + effect == SPELL_EFFECT_APPLY_AREA_AURA_OWNER) + return true; + return false; +} + +inline bool IsDispelSpell(SpellEntry const *spellInfo) +{ + if (spellInfo->Effect[0] == SPELL_EFFECT_DISPEL || + spellInfo->Effect[1] == SPELL_EFFECT_DISPEL || + spellInfo->Effect[2] == SPELL_EFFECT_DISPEL ) + return true; + return false; +} +inline bool isSpellBreakStealth(SpellEntry const* spellInfo) +{ + return !(spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_BREAK_STEALTH); +} + +uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form); + +inline bool IsChanneledSpell(SpellEntry const* spellInfo) +{ + return (spellInfo->AttributesEx & (SPELL_ATTR_EX_CHANNELED_1 | SPELL_ATTR_EX_CHANNELED_2)); +} + +inline bool NeedsComboPoints(SpellEntry const* spellInfo) +{ + return (spellInfo->AttributesEx & (SPELL_ATTR_EX_REQ_COMBO_POINTS1 | SPELL_ATTR_EX_REQ_COMBO_POINTS2)); +} + +inline SpellSchoolMask GetSpellSchoolMask(SpellEntry const* spellInfo) +{ + return SpellSchoolMask(spellInfo->SchoolMask); +} + +inline uint32 GetSpellMechanicMask(SpellEntry const* spellInfo, int32 effect) +{ + uint32 mask = 0; + if (spellInfo->Mechanic) + mask |= 1<Mechanic; + if (spellInfo->EffectMechanic[effect]) + mask |= 1<EffectMechanic[effect]; + return mask; +} + +inline Mechanics GetEffectMechanic(SpellEntry const* spellInfo, int32 effect) +{ + if (spellInfo->EffectMechanic[effect]) + return Mechanics(spellInfo->EffectMechanic[effect]); + if (spellInfo->Mechanic) + return Mechanics(spellInfo->Mechanic); + return MECHANIC_NONE; +} + +inline uint32 GetDispellMask(DispelType dispel) +{ + // If dispell all + if (dispel == DISPEL_ALL) + return DISPEL_ALL_MASK; + else + return (1 << dispel); +} + +// Diminishing Returns interaction with spells +DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered); +bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group); +DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group); + +// Spell affects related declarations (accessed using SpellMgr functions) +typedef std::map SpellAffectMap; + +// Spell proc event related declarations (accessed using SpellMgr functions) +enum ProcFlags +{ + PROC_FLAG_NONE = 0x00000000, // None + PROC_FLAG_HIT_MELEE = 0x00000001, // On melee hit + PROC_FLAG_STRUCK_MELEE = 0x00000002, // On being struck melee + PROC_FLAG_KILL_XP_GIVER = 0x00000004, // On kill target giving XP or honor + PROC_FLAG_SPECIAL_DROP = 0x00000008, // + PROC_FLAG_DODGE = 0x00000010, // On dodge melee attack + PROC_FLAG_PARRY = 0x00000020, // On parry melee attack + PROC_FLAG_BLOCK = 0x00000040, // On block attack + PROC_FLAG_TOUCH = 0x00000080, // On being touched (for bombs, probably?) + PROC_FLAG_TARGET_LOW_HEALTH = 0x00000100, // On deal damage to enemy with 20% or less health + PROC_FLAG_LOW_HEALTH = 0x00000200, // On health dropped below 20% + PROC_FLAG_STRUCK_RANGED = 0x00000400, // On being struck ranged + PROC_FLAG_HIT_SPECIAL = 0x00000800, // (!)Removed, may be reassigned in future + PROC_FLAG_CRIT_MELEE = 0x00001000, // On crit melee + PROC_FLAG_STRUCK_CRIT_MELEE = 0x00002000, // On being critically struck in melee + PROC_FLAG_CAST_SPELL = 0x00004000, // On cast spell + PROC_FLAG_TAKE_DAMAGE = 0x00008000, // On take damage + PROC_FLAG_CRIT_SPELL = 0x00010000, // On crit spell + PROC_FLAG_HIT_SPELL = 0x00020000, // On hit spell + PROC_FLAG_STRUCK_CRIT_SPELL = 0x00040000, // On being critically struck by a spell + PROC_FLAG_HIT_RANGED = 0x00080000, // On getting ranged hit + PROC_FLAG_STRUCK_SPELL = 0x00100000, // On being struck by a spell + PROC_FLAG_TRAP = 0x00200000, // On trap activation (?) + PROC_FLAG_CRIT_RANGED = 0x00400000, // On getting ranged crit + PROC_FLAG_STRUCK_CRIT_RANGED = 0x00800000, // On being critically struck by a ranged attack + PROC_FLAG_RESIST_SPELL = 0x01000000, // On resist enemy spell + PROC_FLAG_TARGET_RESISTS = 0x02000000, // On enemy resisted spell + PROC_FLAG_TARGET_DODGE_OR_PARRY = 0x04000000, // On enemy dodges/parries + PROC_FLAG_HEAL = 0x08000000, // On heal + PROC_FLAG_CRIT_HEAL = 0x10000000, // On critical healing effect + PROC_FLAG_HEALED = 0x20000000, // On healing + PROC_FLAG_TARGET_BLOCK = 0x40000000, // On enemy blocks + PROC_FLAG_MISS = 0x80000000 // On miss melee attack +}; + +struct SpellProcEventEntry +{ + uint32 schoolMask; // if nonzero - bit mask for matching proc condition based on spell candidate's school: Fire=2, Mask=1<<(2-1)=2 + uint32 category; // if nonzero - match proc condition based on candidate spell's category + uint32 skillId; // if nonzero - for matching proc condition based on candidate spell's skillId from SkillLineAbility.dbc (Shadow Bolt = Destruction) + uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyNamer value + uint64 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do) + uint32 procFlags; // bitmask for matching proc event + float ppmRate; // for melee (ranged?) damage spells - proc rate per minute. if zero, falls back to flat chance from Spell.dbc + uint32 cooldown; // hidden cooldown used for some spell proc events, applied to _triggered_spell_ +}; + +typedef HM_NAMESPACE::hash_map SpellProcEventMap; + +#define ELIXIR_BATTLE_MASK 0x1 +#define ELIXIR_GUARDIAN_MASK 0x2 +#define ELIXIR_FLASK_MASK (ELIXIR_BATTLE_MASK|ELIXIR_GUARDIAN_MASK) +#define ELIXIR_UNSTABLE_MASK 0x4 +#define ELIXIR_SHATTRATH_MASK 0x8 + +typedef std::map SpellElixirMap; + +// Spell script target related declarations (accessed using SpellMgr functions) +enum SpellTargetType +{ + SPELL_TARGET_TYPE_GAMEOBJECT = 0, + SPELL_TARGET_TYPE_CREATURE = 1, + SPELL_TARGET_TYPE_DEAD = 2 +}; + +#define MAX_SPELL_TARGET_TYPE 3 + +struct SpellTargetEntry +{ + SpellTargetEntry(SpellTargetType type_,uint32 targetEntry_) : type(type_), targetEntry(targetEntry_) {} + SpellTargetType type; + uint32 targetEntry; +}; + +typedef std::multimap SpellScriptTarget; + +// coordinates for spells (accessed using SpellMgr functions) +struct SpellTargetPosition +{ + uint32 target_mapId; + float target_X; + float target_Y; + float target_Z; + float target_Orientation; +}; + +typedef HM_NAMESPACE::hash_map SpellTargetPositionMap; + +// Spell pet auras +class PetAura +{ + public: + PetAura() + { + auras.clear(); + } + + PetAura(uint16 petEntry, uint16 aura, bool _removeOnChangePet, int _damage) : + removeOnChangePet(_removeOnChangePet), damage(_damage) + { + auras[petEntry] = aura; + } + + uint16 GetAura(uint16 petEntry) const + { + std::map::const_iterator itr = auras.find(petEntry); + if(itr != auras.end()) + return itr->second; + else + { + std::map::const_iterator itr = auras.find(0); + if(itr != auras.end()) + return itr->second; + else + return 0; + } + } + + void AddAura(uint16 petEntry, uint16 aura) + { + auras[petEntry] = aura; + } + + bool IsRemovedOnChangePet() const + { + return removeOnChangePet; + } + + int32 GetDamage() const + { + return damage; + } + + private: + std::map auras; + bool removeOnChangePet; + int32 damage; +}; +typedef std::map SpellPetAuraMap; + +// Spell rank chain (accessed using SpellMgr functions) +struct SpellChainNode +{ + uint32 prev; + uint32 first; + uint32 req; + uint8 rank; +}; + +typedef HM_NAMESPACE::hash_map SpellChainMap; +typedef std::multimap SpellChainMapNext; + +// Spell learning properties (accessed using SpellMgr functions) +struct SpellLearnSkillNode +{ + uint32 skill; + uint32 value; // 0 - max skill value for player level + uint32 maxvalue; // 0 - max skill value for player level +}; + +typedef std::map SpellLearnSkillMap; + +struct SpellLearnSpellNode +{ + uint32 spell; + bool autoLearned; +}; + +typedef std::multimap SpellLearnSpellMap; + +typedef std::multimap SkillLineAbilityMap; + +inline bool IsPrimaryProfessionSkill(uint32 skill) +{ + SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(skill); + if(!pSkill) + return false; + + if(pSkill->categoryId != SKILL_CATEGORY_PROFESSION) + return false; + + return true; +} + +inline bool IsProfessionSkill(uint32 skill) +{ + return IsPrimaryProfessionSkill(skill) || skill == SKILL_FISHING || skill == SKILL_COOKING || skill == SKILL_FIRST_AID; +} + +class SpellMgr +{ + // Constructors + public: + SpellMgr(); + ~SpellMgr(); + + // Accessors (const or static functions) + public: + // Spell affects + uint64 GetSpellAffectMask(uint16 spellId, uint8 effectId) const + { + SpellAffectMap::const_iterator itr = mSpellAffectMap.find((spellId<<8) + effectId); + if( itr != mSpellAffectMap.end( ) ) + return itr->second; + return 0; + } + + bool IsAffectedBySpell(SpellEntry const *spellInfo, uint32 spellId, uint8 effectId, uint64 familyFlags) const; + + SpellElixirMap const& GetSpellElixirMap() const { return mSpellElixirs; } + + uint32 GetSpellElixirMask(uint32 spellid) const + { + SpellElixirMap::const_iterator itr = mSpellElixirs.find(spellid); + if(itr==mSpellElixirs.end()) + return 0x0; + + return itr->second; + } + + SpellSpecific GetSpellElixirSpecific(uint32 spellid) const + { + uint32 mask = GetSpellElixirMask(spellid); + if((mask & ELIXIR_FLASK_MASK)==ELIXIR_FLASK_MASK) + return SPELL_FLASK_ELIXIR; + else if(mask & ELIXIR_BATTLE_MASK) + return SPELL_BATTLE_ELIXIR; + else if(mask & ELIXIR_GUARDIAN_MASK) + return SPELL_GUARDIAN_ELIXIR; + else + return SPELL_NORMAL; + } + + // Spell proc events + SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const + { + SpellProcEventMap::const_iterator itr = mSpellProcEventMap.find(spellId); + if( itr != mSpellProcEventMap.end( ) ) + return &itr->second; + return NULL; + } + + static bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, SpellEntry const * procSpell, uint32 procFlags ); + + // Spell target coordinates + SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id) const + { + SpellTargetPositionMap::const_iterator itr = mSpellTargetPositions.find( spell_id ); + if( itr != mSpellTargetPositions.end( ) ) + return &itr->second; + return NULL; + } + + // Spell ranks chains + SpellChainNode const* GetSpellChainNode(uint32 spell_id) const + { + SpellChainMap::const_iterator itr = mSpellChains.find(spell_id); + if(itr == mSpellChains.end()) + return NULL; + + return &itr->second; + } + + uint32 GetFirstSpellInChain(uint32 spell_id) const + { + if(SpellChainNode const* node = GetSpellChainNode(spell_id)) + return node->first; + + return spell_id; + } + + uint32 GetPrevSpellInChain(uint32 spell_id) const + { + if(SpellChainNode const* node = GetSpellChainNode(spell_id)) + return node->prev; + + return 0; + } + + SpellChainMapNext const& GetSpellChainNext() const { return mSpellChainsNext; } + + // Note: not use rank for compare to spell ranks: spell chains isn't linear order + // Use IsHighRankOfSpell instead + uint8 GetSpellRank(uint32 spell_id) const + { + if(SpellChainNode const* node = GetSpellChainNode(spell_id)) + return node->rank; + + return 0; + } + + uint8 IsHighRankOfSpell(uint32 spell1,uint32 spell2) const + { + SpellChainMap::const_iterator itr = mSpellChains.find(spell1); + + uint32 rank2 = GetSpellRank(spell2); + + // not ordered correctly by rank value + if(itr == mSpellChains.end() || !rank2 || itr->second.rank <= rank2) + return false; + + // check present in same rank chain + for(; itr != mSpellChains.end(); itr = mSpellChains.find(itr->second.prev)) + if(itr->second.prev==spell2) + return true; + + return false; + } + + bool IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const; + static bool canStackSpellRanks(SpellEntry const *spellInfo); + bool IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) const; + + SpellEntry const* SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const; + + // Spell learning + SpellLearnSkillNode const* GetSpellLearnSkill(uint32 spell_id) const + { + SpellLearnSkillMap::const_iterator itr = mSpellLearnSkills.find(spell_id); + if(itr != mSpellLearnSkills.end()) + return &itr->second; + else + return NULL; + } + + bool IsSpellLearnSpell(uint32 spell_id) const + { + return mSpellLearnSpells.count(spell_id)!=0; + } + + SpellLearnSpellMap::const_iterator GetBeginSpellLearnSpell(uint32 spell_id) const + { + return mSpellLearnSpells.lower_bound(spell_id); + } + + SpellLearnSpellMap::const_iterator GetEndSpellLearnSpell(uint32 spell_id) const + { + return mSpellLearnSpells.upper_bound(spell_id); + } + + bool IsSpellLearnToSpell(uint32 spell_id1,uint32 spell_id2) const + { + SpellLearnSpellMap::const_iterator b = GetBeginSpellLearnSpell(spell_id1); + SpellLearnSpellMap::const_iterator e = GetEndSpellLearnSpell(spell_id1); + for(SpellLearnSpellMap::const_iterator i = b; i != e; ++i) + if(i->second.spell==spell_id2) + return true; + return false; + } + + static bool IsProfessionSpell(uint32 spellId); + static bool IsPrimaryProfessionSpell(uint32 spellId); + bool IsPrimaryProfessionFirstRankSpell(uint32 spellId) const; + + // Spell script targets + SpellScriptTarget::const_iterator GetBeginSpellScriptTarget(uint32 spell_id) const + { + return mSpellScriptTarget.lower_bound(spell_id); + } + + SpellScriptTarget::const_iterator GetEndSpellScriptTarget(uint32 spell_id) const + { + return mSpellScriptTarget.upper_bound(spell_id); + } + + // Spell correctess for client using + static bool IsSpellValid(SpellEntry const * spellInfo, Player* pl = NULL, bool msg = true); + + SkillLineAbilityMap::const_iterator GetBeginSkillLineAbilityMap(uint32 spell_id) const + { + return mSkillLineAbilityMap.lower_bound(spell_id); + } + + SkillLineAbilityMap::const_iterator GetEndSkillLineAbilityMap(uint32 spell_id) const + { + return mSkillLineAbilityMap.upper_bound(spell_id); + } + + PetAura const* GetPetAura(uint16 spell_id) + { + SpellPetAuraMap::const_iterator itr = mSpellPetAuraMap.find(spell_id); + if(itr != mSpellPetAuraMap.end()) + return &itr->second; + else + return NULL; + } + + // Modifiers + public: + static SpellMgr& Instance(); + + // Loading data at server startup + void LoadSpellChains(); + void LoadSpellLearnSkills(); + void LoadSpellLearnSpells(); + void LoadSpellScriptTarget(); + void LoadSpellAffects(); + void LoadSpellElixirs(); + void LoadSpellProcEvents(); + void LoadSpellTargetPositions(); + void LoadSpellThreats(); + void LoadSkillLineAbilityMap(); + void LoadSpellPetAuras(); + + private: + SpellScriptTarget mSpellScriptTarget; + SpellChainMap mSpellChains; + SpellChainMapNext mSpellChainsNext; + SpellLearnSkillMap mSpellLearnSkills; + SpellLearnSpellMap mSpellLearnSpells; + SpellTargetPositionMap mSpellTargetPositions; + SpellAffectMap mSpellAffectMap; + SpellElixirMap mSpellElixirs; + SpellProcEventMap mSpellProcEventMap; + SkillLineAbilityMap mSkillLineAbilityMap; + SpellPetAuraMap mSpellPetAuraMap; +}; + +#define spellmgr SpellMgr::Instance() +#endif diff --git a/src/game/StatSystem.cpp b/src/game/StatSystem.cpp new file mode 100644 index 00000000000..b89346d937a --- /dev/null +++ b/src/game/StatSystem.cpp @@ -0,0 +1,955 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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() +{ + // Base value + float 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() +{ + // Base parry + float 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<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 new file mode 100644 index 00000000000..bba7426938d --- /dev/null +++ b/src/game/TargetedMovementGenerator.cpp @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 +/* +struct StackCleaner +{ + Creature &i_creature; + StackCleaner(Creature &creature) : i_creature(creature) {} + void Done(void) { i_creature.StopMoving(); } + ~StackCleaner() + { + i_creature->Clear(); + } +}; +*/ + +template +void +TargetedMovementGenerator::_setTargetLocation(T &owner) +{ + if( !i_target.isValid() || !&owner ) + return; + + if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | 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::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 traveller(owner); + i_destinationHolder.SetDestination(traveller, x, y, z); + owner.addUnitState(UNIT_STAT_CHASE); +} + +template +void +TargetedMovementGenerator::Initialize(T &owner) +{ + if(!&owner) + return; + owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); + _setTargetLocation(owner); +} + +template +void +TargetedMovementGenerator::Finalize(T &owner) +{ + owner.clearUnitState(UNIT_STAT_CHASE); +} + +template +void +TargetedMovementGenerator::Reset(T &owner) +{ + Initialize(owner); +} + +template +bool +TargetedMovementGenerator::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_STUNDED | 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 traveller(owner); + + if( !i_destinationHolder.HasDestination() ) + _setTargetLocation(owner); + if( owner.IsStopped() && !i_destinationHolder.HasArrived() ) + { + owner.addUnitState(UNIT_STAT_CHASE); + 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 +Unit* +TargetedMovementGenerator::GetTarget() const +{ + return i_target.getTarget(); +} + +template void TargetedMovementGenerator::_setTargetLocation(Player &); +template void TargetedMovementGenerator::_setTargetLocation(Creature &); +template void TargetedMovementGenerator::Initialize(Player &); +template void TargetedMovementGenerator::Initialize(Creature &); +template void TargetedMovementGenerator::Finalize(Player &); +template void TargetedMovementGenerator::Finalize(Creature &); +template void TargetedMovementGenerator::Reset(Player &); +template void TargetedMovementGenerator::Reset(Creature &); +template bool TargetedMovementGenerator::Update(Player &, const uint32 &); +template bool TargetedMovementGenerator::Update(Creature &, const uint32 &); +template Unit* TargetedMovementGenerator::GetTarget() const; +template Unit* TargetedMovementGenerator::GetTarget() const; diff --git a/src/game/TargetedMovementGenerator.h b/src/game/TargetedMovementGenerator.h new file mode 100644 index 00000000000..725f07224ba --- /dev/null +++ b/src/game/TargetedMovementGenerator.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_TARGETEDMOVEMENTGENERATOR_H +#define MANGOS_TARGETEDMOVEMENTGENERATOR_H + +#include "MovementGenerator.h" +#include "DestinationHolder.h" +#include "Traveller.h" +#include "FollowerReference.h" + +class MANGOS_DLL_SPEC TargetedMovementGeneratorBase +{ + public: + TargetedMovementGeneratorBase(Unit &target) { i_target.link(&target, this); } + void stopFollowing() { } + protected: + FollowerReference i_target; +}; + +template +class MANGOS_DLL_SPEC TargetedMovementGenerator +: public MovementGeneratorMedium< T, TargetedMovementGenerator >, public TargetedMovementGeneratorBase +{ + public: + + TargetedMovementGenerator(Unit &target) + : TargetedMovementGeneratorBase(target), i_offset(0), i_angle(0), i_recalculateTravel(false) {} + TargetedMovementGenerator(Unit &target, float offset, float angle) + : TargetedMovementGeneratorBase(target), i_offset(offset), i_angle(angle), i_recalculateTravel(false) {} + ~TargetedMovementGenerator() {} + + void Initialize(T &); + void Finalize(T &); + void Reset(T &); + bool Update(T &, const uint32 &); + MovementGeneratorType GetMovementGeneratorType() { return TARGETED_MOTION_TYPE; } + + Unit* GetTarget() const; + + bool GetDestination(float &x, float &y, float &z) const + { + if(!i_destinationHolder.HasDestination()) return false; + i_destinationHolder.GetDestination(x,y,z); + return true; + } + + void unitSpeedChanged() { i_recalculateTravel=true; } + private: + + void _setTargetLocation(T &); + + float i_offset; + float i_angle; + DestinationHolder< Traveller > i_destinationHolder; + bool i_recalculateTravel; +}; +#endif diff --git a/src/game/TaxiHandler.cpp b/src/game/TaxiHandler.cpp new file mode 100644 index 00000000000..e804bec8463 --- /dev/null +++ b/src/game/TaxiHandler.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.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 "Path.h" +#include "WaypointMovementGenerator.h" +#include "DestinationHolderImp.h" + +#include + +void WorldSession::HandleTaxiNodeStatusQueryOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + sLog.outDebug( "WORLD: Received CMSG_TAXINODE_STATUS_QUERY" ); + + uint64 guid; + + recv_data >> guid; + SendTaxiStatus( guid ); +} + +void WorldSession::SendTaxiStatus( uint64 guid ) +{ + // cheating checks + Creature *unit = ObjectAccessor::GetCreature(*_player, guid); + if (!unit) + { + sLog.outDebug( "WorldSession::SendTaxiStatus - Unit (GUID: %u) not found.", uint32(GUID_LOPART(guid)) ); + return; + } + + uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId()); + + // not found nearest + if(curloc == 0) + return; + + sLog.outDebug( "WORLD: current location %u ",curloc); + + WorldPacket data( SMSG_TAXINODE_STATUS, 9 ); + data << guid; + data << uint8( GetPlayer( )->m_taxi.IsTaximaskNodeKnown(curloc) ? 1 : 0 ); + SendPacket( &data ); + sLog.outDebug( "WORLD: Sent SMSG_TAXINODE_STATUS" ); +} + +void WorldSession::HandleTaxiQueryAvailableNodesOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8); + + sLog.outDebug( "WORLD: Received CMSG_TAXIQUERYAVAILABLENODES" ); + + uint64 guid; + recv_data >> guid; + + // cheating checks + Creature *unit = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_FLIGHTMASTER); + if (!unit) + { + sLog.outDebug( "WORLD: HandleTaxiQueryAvailableNodesOpcode - 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); + + // unknown taxi node case + if( SendLearnNewTaxiNode(unit) ) + return; + + // known taxi node case + SendTaxiMenu( unit ); +} + +void WorldSession::SendTaxiMenu( Creature* unit ) +{ + // find current node + uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId()); + + if ( curloc == 0 ) + return; + + sLog.outDebug( "WORLD: CMSG_TAXINODE_STATUS_QUERY %u ",curloc); + + WorldPacket data( SMSG_SHOWTAXINODES, (4+8+4+8*4) ); + data << uint32( 1 ); + data << uint64( unit->GetGUID() ); + data << uint32( curloc ); + GetPlayer()->m_taxi.AppendTaximaskTo(data,GetPlayer()->isTaxiCheater()); + SendPacket( &data ); + + sLog.outDebug( "WORLD: Sent SMSG_SHOWTAXINODES" ); +} + +void WorldSession::SendDoFlight( uint16 MountId, uint32 path, uint32 pathNode ) +{ + // remove fake death + if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) + GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); + + while(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()==FLIGHT_MOTION_TYPE) + GetPlayer()->GetMotionMaster()->MovementExpired(false); + + GetPlayer()->Mount( MountId ); + GetPlayer()->GetMotionMaster()->MoveTaxiFlight(path,pathNode); +} + +bool WorldSession::SendLearnNewTaxiNode( Creature* unit ) +{ + // find current node + uint32 curloc = objmgr.GetNearestTaxiNode(unit->GetPositionX(),unit->GetPositionY(),unit->GetPositionZ(),unit->GetMapId()); + + if ( curloc == 0 ) + return true; // `true` send to avoid WorldSession::SendTaxiMenu call with one more curlock seartch with same false result. + + if( GetPlayer()->m_taxi.SetTaximaskNode(curloc) ) + { + WorldPacket msg(SMSG_NEW_TAXI_PATH, 0); + SendPacket( &msg ); + + WorldPacket update( SMSG_TAXINODE_STATUS, 9 ); + update << uint64( unit->GetGUID() ); + update << uint8( 1 ); + SendPacket( &update ); + + return true; + } + else + return false; +} + +void WorldSession::HandleActivateTaxiFarOpcode ( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4+4); + + sLog.outDebug( "WORLD: Received CMSG_ACTIVATETAXIEXPRESS" ); + + uint64 guid; + uint32 node_count, _totalcost; + + recv_data >> guid >> _totalcost >> node_count; + + Creature *npc = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_FLIGHTMASTER); + if (!npc) + { + sLog.outDebug( "WORLD: HandleActivateTaxiFarOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)) ); + return; + } + // recheck + CHECK_PACKET_SIZE(recv_data,8+4+4+node_count*4); + + std::vector nodes; + + for(uint32 i = 0; i < node_count; ++i) + { + uint32 node; + recv_data >> node; + nodes.push_back(node); + } + + if(nodes.empty()) + return; + + sLog.outDebug( "WORLD: Received CMSG_ACTIVATETAXIEXPRESS from %d to %d" ,nodes.front(),nodes.back()); + + GetPlayer()->ActivateTaxiPathTo(nodes, 0, npc); +} + +void WorldSession::HandleTaxiNextDestinationOpcode(WorldPacket& /*recv_data*/) +{ + sLog.outDebug( "WORLD: Received CMSG_MOVE_SPLINE_DONE" ); + + // in taxi flight packet received in 2 case: + // 1) end taxi path in far (multi-node) flight + // 2) switch from one map to other in case multim-map taxi path + // we need proccess only (1) + uint32 curDest = GetPlayer()->m_taxi.GetTaxiDestination(); + if(!curDest) + return; + + TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest); + + // far teleport case + if(curDestNode && curDestNode->map_id != GetPlayer()->GetMapId()) + { + if(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()==FLIGHT_MOTION_TYPE) + { + // short preparations to continue flight + FlightPathMovementGenerator* flight = (FlightPathMovementGenerator*)(GetPlayer()->GetMotionMaster()->top()); + + flight->SetCurrentNodeAfterTeleport(); + Path::PathNode const& node = flight->GetPath()[flight->GetCurrentNode()]; + flight->SkipCurrentNode(); + + GetPlayer()->TeleportTo(curDestNode->map_id,node.x,node.y,node.z,GetPlayer()->GetOrientation()); + } + return; + } + + uint32 destinationnode = GetPlayer()->m_taxi.NextTaxiDestination(); + if ( destinationnode > 0 ) // if more destinations to go + { + // current source node for next destination + uint32 sourcenode = GetPlayer()->m_taxi.GetTaxiSource(); + + // Add to taximask middle hubs in taxicheat mode (to prevent having player with disabled taxicheat and not having back flight path) + if (GetPlayer()->isTaxiCheater()) + { + if(GetPlayer()->m_taxi.SetTaximaskNode(sourcenode)) + { + WorldPacket data(SMSG_NEW_TAXI_PATH, 0); + _player->GetSession()->SendPacket( &data ); + } + } + + sLog.outDebug( "WORLD: Taxi has to go from %u to %u", sourcenode, destinationnode ); + + uint16 MountId = objmgr.GetTaxiMount(sourcenode, GetPlayer()->GetTeam()); + + uint32 path, cost; + objmgr.GetTaxiPath( sourcenode, destinationnode, path, cost); + + if(path && MountId) + SendDoFlight( MountId, path, 1 ); // skip start fly node + else + GetPlayer()->m_taxi.ClearTaxiDestinations(); // clear problematic path and next + } + else + GetPlayer()->m_taxi.ClearTaxiDestinations(); // not destinations, clear source node +} + +void WorldSession::HandleActivateTaxiOpcode( WorldPacket & recv_data ) +{ + CHECK_PACKET_SIZE(recv_data,8+4+4); + + sLog.outDebug( "WORLD: Received CMSG_ACTIVATETAXI" ); + + uint64 guid; + std::vector nodes; + nodes.resize(2); + + recv_data >> guid >> nodes[0] >> nodes[1]; + sLog.outDebug( "WORLD: Received CMSG_ACTIVATETAXI from %d to %d" ,nodes[0],nodes[1]); + Creature *npc = ObjectAccessor::GetNPCIfCanInteractWith(*_player, guid, UNIT_NPC_FLAG_FLIGHTMASTER); + if (!npc) + { + sLog.outDebug( "WORLD: HandleActivateTaxiOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)) ); + return; + } + + GetPlayer()->ActivateTaxiPathTo(nodes, 0, npc); +} diff --git a/src/game/TemporarySummon.cpp b/src/game/TemporarySummon.cpp new file mode 100644 index 00000000000..130c4a7904a --- /dev/null +++ b/src/game/TemporarySummon.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "TemporarySummon.h" +#include "WorldPacket.h" +#include "MapManager.h" +#include "Log.h" +#include "ObjectAccessor.h" +#include "CreatureAI.h" + +TemporarySummon::TemporarySummon( uint64 summoner ) : +Creature(), m_type(TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN), m_timer(0), m_lifetime(0), m_summoner(summoner) +{ +} + +void TemporarySummon::Update( uint32 diff ) +{ + switch(m_type) + { + case TEMPSUMMON_MANUAL_DESPAWN: + break; + case TEMPSUMMON_TIMED_DESPAWN: + { + if (m_timer <= diff) + { + UnSummon(); + return; + } + + m_timer -= diff; + break; + } + case TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT: + { + if (!isInCombat()) + { + if (m_timer <= diff) + { + UnSummon(); + return; + } + + m_timer -= diff; + } + else if (m_timer != m_lifetime) + m_timer = m_lifetime; + + break; + } + + case TEMPSUMMON_CORPSE_TIMED_DESPAWN: + { + if ( m_deathState == CORPSE) + { + if (m_timer <= diff) + { + UnSummon(); + return; + } + + m_timer -= diff; + } + break; + } + case TEMPSUMMON_CORPSE_DESPAWN: + { + // if m_deathState is DEAD, CORPSE was skipped + if ( m_deathState == CORPSE || m_deathState == DEAD) + { + UnSummon(); + return; + } + + break; + } + case TEMPSUMMON_DEAD_DESPAWN: + { + if ( m_deathState == DEAD ) + { + UnSummon(); + return; + } + break; + } + case TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN: + { + // if m_deathState is DEAD, CORPSE was skipped + if ( m_deathState == CORPSE || m_deathState == DEAD) + { + UnSummon(); + return; + } + + if (!isInCombat()) + { + if (m_timer <= diff) + { + UnSummon(); + return; + } + else + m_timer -= diff; + } + else if (m_timer != m_lifetime) + m_timer = m_lifetime; + break; + } + case TEMPSUMMON_TIMED_OR_DEAD_DESPAWN: + { + // if m_deathState is DEAD, CORPSE was skipped + if (m_deathState == DEAD) + { + UnSummon(); + return; + } + + if (!isInCombat() && isAlive() ) + { + if (m_timer <= diff) + { + UnSummon(); + return; + } + else + m_timer -= diff; + } + else if (m_timer != m_lifetime) + m_timer = m_lifetime; + break; + } + default: + UnSummon(); + sLog.outError("Temporary summoned creature (entry: %u) have unknown type %u of ",GetEntry(),m_type); + break; + } + + Creature::Update( diff ); +} + +void TemporarySummon::Summon(TempSummonType type, uint32 lifetime) +{ + m_type = type; + m_timer = lifetime; + m_lifetime = lifetime; + + MapManager::Instance().GetMap(GetMapId(), this)->Add((Creature*)this); + + AIM_Initialize(); +} + +void TemporarySummon::UnSummon() +{ + CombatStop(); + + CleanupsBeforeDelete(); + AddObjectToRemoveList(); + + Unit* sum = m_summoner ? ObjectAccessor::GetUnit(*this, m_summoner) : NULL; + if (sum && sum->GetTypeId() == TYPEID_UNIT && ((Creature*)sum)->AI()) + { + ((Creature*)sum)->AI()->SummonedCreatureDespawn(this); + } +} + +void TemporarySummon::SaveToDB() +{ +} diff --git a/src/game/TemporarySummon.h b/src/game/TemporarySummon.h new file mode 100644 index 00000000000..8007b853938 --- /dev/null +++ b/src/game/TemporarySummon.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_TEMPSUMMON_H +#define MANGOSSERVER_TEMPSUMMON_H + +#include "Creature.h" +#include "ObjectAccessor.h" + +class TemporarySummon : public Creature +{ + public: + explicit TemporarySummon(uint64 summoner = 0); + virtual ~TemporarySummon(){}; + void Update(uint32 time); + void Summon(TempSummonType type, uint32 lifetime); + void UnSummon(); + void SaveToDB(); + Unit* GetSummoner() const { return m_summoner ? ObjectAccessor::GetUnit(*this, m_summoner) : NULL; } + private: + TempSummonType m_type; + uint32 m_timer; + uint32 m_lifetime; + uint64 m_summoner; +}; +#endif diff --git a/src/game/ThreatManager.cpp b/src/game/ThreatManager.cpp new file mode 100644 index 00000000000..b9fd7a44f87 --- /dev/null +++ b/src/game/ThreatManager.cpp @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ThreatManager.h" +#include "Unit.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "Map.h" +#include "MapManager.h" +#include "Player.h" +#include "ObjectAccessor.h" +#include "UnitEvents.h" + +//============================================================== +//================= ThreatCalcHelper =========================== +//============================================================== + +// The pHatingUnit is not used yet +float ThreatCalcHelper::calcThreat(Unit* pHatedUnit, Unit* pHatingUnit, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell) +{ + if(pThreatSpell) + { + if( Player* modOwner = pHatingUnit->GetSpellModOwner() ) + modOwner->ApplySpellMod(pThreatSpell->Id, SPELLMOD_THREAT, pThreat); + } + + float threat = pHatedUnit->ApplyTotalThreatModifier(pThreat, schoolMask); + return threat; +} + +//============================================================ +//================= HostilReference ========================== +//============================================================ + +HostilReference::HostilReference(Unit* pUnit, ThreatManager *pThreatManager, float pThreat) +{ + iThreat = pThreat; + iTempThreatModifyer = 0.0f; + link(pUnit, pThreatManager); + iUnitGuid = pUnit->GetGUID(); + iOnline = true; + iAccessible = true; +} + +//============================================================ +// Tell our refTo (target) object that we have a link +void HostilReference::targetObjectBuildLink() +{ + getTarget()->addHatedBy(this); +} + +//============================================================ +// Tell our refTo (taget) object, that the link is cut +void HostilReference::targetObjectDestroyLink() +{ + getTarget()->removeHatedBy(this); +} + +//============================================================ +// Tell our refFrom (source) object, that the link is cut (Target destroyed) + +void HostilReference::sourceObjectDestroyLink() +{ + setOnlineOfflineState(false); +} + +//============================================================ +// Inform the source, that the status of the reference changed + +void HostilReference::fireStatusChanged(const ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent) +{ + if(getSource()) + getSource()->processThreatEvent(&pThreatRefStatusChangeEvent); +} + +//============================================================ + +void HostilReference::addThreat(float pMod) +{ + iThreat += pMod; + // the threat is changed. Source and target unit have to be availabe + // if the link was cut before relink it again + if(!isOnline()) + updateOnlineStatus(); + if(pMod != 0.0f) + fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_THREAT_CHANGE, this, pMod)); + if(isValid() && pMod >= 0) + { + Unit* victim_owner = getTarget()->GetOwner(); + if(victim_owner && victim_owner->isAlive()) + getSource()->addThreat(victim_owner, 0.0f); // create a threat to the owner of a pet, if the pet attacks + } +} + +//============================================================ +// check, if source can reach target and set the status + +void HostilReference::updateOnlineStatus() +{ + bool online = false; + bool accessible = false; + + if(!isValid()) + { + Unit* target = ObjectAccessor::GetUnit(*getSourceUnit(), getUnitGuid()); + if(target) + link(target, getSource()); + } + // only check for online status if + // ref is valid + // target is no player or not gamemaster + // target is not in flight + if(isValid() && + ((getTarget()->GetTypeId() != TYPEID_PLAYER || !((Player*)getTarget())->isGameMaster()) || + !getTarget()->hasUnitState(UNIT_STAT_IN_FLIGHT))) + { + Creature* creature = (Creature* ) getSourceUnit(); + online = getTarget()->isInAccessablePlaceFor(creature); + if(!online) + { + if(creature->AI()->canReachByRangeAttack(getTarget())) + online = true; // not accessable but stays online + } + else + accessible = true; + + } + setAccessibleState(accessible); + setOnlineOfflineState(online); +} + +//============================================================ +// set the status and fire the event on status change + +void HostilReference::setOnlineOfflineState(bool pIsOnline) +{ + if(iOnline != pIsOnline) + { + iOnline = pIsOnline; + if(!iOnline) + setAccessibleState(false); // if not online that not accessable as well + fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ONLINE_STATUS, this)); + } +} + +//============================================================ + +void HostilReference::setAccessibleState(bool pIsAccessible) +{ + if(iAccessible != pIsAccessible) + { + iAccessible = pIsAccessible; + fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ASSECCIBLE_STATUS, this)); + } +} + +//============================================================ +// prepare the reference for deleting +// this is called be the target + +void HostilReference::removeReference() +{ + invalidate(); + fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_REMOVE_FROM_LIST, this)); +} + +//============================================================ + +Unit* HostilReference::getSourceUnit() +{ + return (getSource()->getOwner()); +} + +//============================================================ +//================ ThreatContainer =========================== +//============================================================ + +void ThreatContainer::clearReferences() +{ + for(std::list::iterator i = iThreatList.begin(); i != iThreatList.end(); i++) + { + (*i)->unlink(); + delete (*i); + } + iThreatList.clear(); +} + +//============================================================ +// Return the HostilReference of NULL, if not found +HostilReference* ThreatContainer::getReferenceByTarget(Unit* pVictim) +{ + HostilReference* result = NULL; + uint64 guid = pVictim->GetGUID(); + for(std::list::iterator i = iThreatList.begin(); i != iThreatList.end(); i++) + { + if((*i)->getUnitGuid() == guid) + { + result = (*i); + break; + } + } + + return result; +} + +//============================================================ +// Add the threat, if we find the reference + +HostilReference* ThreatContainer::addThreat(Unit* pVictim, float pThreat) +{ + HostilReference* ref = getReferenceByTarget(pVictim); + if(ref) + ref->addThreat(pThreat); + return ref; +} + +//============================================================ + +void ThreatContainer::modifyThreatPercent(Unit *pVictim, int32 pPercent) +{ + if(HostilReference* ref = getReferenceByTarget(pVictim)) + ref->addThreatPercent(pPercent); +} + +//============================================================ + +bool HostilReferenceSortPredicate(const HostilReference* lhs, const HostilReference* rhs) +{ + // std::list::sort ordering predicate must be: (Pred(x,y)&&Pred(y,x))==false + return lhs->getThreat() > rhs->getThreat(); // reverse sorting +} + +//============================================================ +// Check if the list is dirty and sort if necessary + +void ThreatContainer::update() +{ + if(iDirty && iThreatList.size() >1) + { + iThreatList.sort(HostilReferenceSortPredicate); + } + iDirty = false; +} + +//============================================================ +// return the next best victim +// could be the current victim + +HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim) +{ + HostilReference* currentRef = NULL; + bool found = false; + for(std::list::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found; ++iter) + { + currentRef = (*iter); + + Unit* target = currentRef->getTarget(); + assert(target); // if the ref has status online the target must be there ! + + if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets + { + if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target + { + // list sorted and and we check current target, then this is best case + if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() ) + { + currentRef = pCurrentVictim; // for second case + found = true; + break; + } + + if( currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() || + currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE) ) + { //implement 110% threat rule for targets in melee range + found = true; //and 130% rule for targets in ranged distances + break; //for selecting alive targets + } + } + else // select any + { + found = true; + break; + } + } + } + if(!found) + currentRef = NULL; + + return currentRef; +} + +//============================================================ +//=================== ThreatManager ========================== +//============================================================ + +ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner) +{ +} + +//============================================================ + +void ThreatManager::clearReferences() +{ + iThreatContainer.clearReferences(); + iThreatOfflineContainer.clearReferences(); + iCurrentVictim = NULL; +} + +//============================================================ + +void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell) +{ + //function deals with adding threat and adding players and pets into ThreatList + //mobs, NPCs, guards have ThreatList and HateOfflineList + //players and pets have only InHateListOf + //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.) + + if (pVictim == getOwner()) // only for same creatures :) + return; + + if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) ) + return; + + assert(getOwner()->GetTypeId()== TYPEID_UNIT); + + float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell); + + HostilReference* ref = iThreatContainer.addThreat(pVictim, threat); + // Ref is not in the online refs, search the offline refs next + if(!ref) + ref = iThreatOfflineContainer.addThreat(pVictim, threat); + + if(!ref) // there was no ref => create a new one + { + // threat has to be 0 here + HostilReference* hostilReference = new HostilReference(pVictim, this, 0); + iThreatContainer.addReference(hostilReference); + hostilReference->addThreat(threat); // now we add the real threat + if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) + hostilReference->setOnlineOfflineState(false); // GM is always offline + } +} + +//============================================================ + +void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 pPercent) +{ + iThreatContainer.modifyThreatPercent(pVictim, pPercent); +} + +//============================================================ + +Unit* ThreatManager::getHostilTarget() +{ + iThreatContainer.update(); + HostilReference* nextVictim = iThreatContainer.selectNextVictim((Creature*) getOwner(), getCurrentVictim()); + setCurrentVictim(nextVictim); + return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL; +} + +//============================================================ + +float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList) +{ + float threat = 0.0f; + HostilReference* ref = iThreatContainer.getReferenceByTarget(pVictim); + if(!ref && pAlsoSearchOfflineList) + ref = iThreatOfflineContainer.getReferenceByTarget(pVictim); + if(ref) + threat = ref->getThreat(); + return threat; +} + +//============================================================ + +void ThreatManager::tauntApply(Unit* pTaunter) +{ + HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter); + if(getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat())) + { + if(ref->getTempThreatModifyer() == 0.0f) + // Ok, temp threat is unused + ref->setTempThreat(getCurrentVictim()->getThreat()); + } +} + +//============================================================ + +void ThreatManager::tauntFadeOut(Unit *pTaunter) +{ + HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter); + if(ref) + ref->resetTempThreat(); +} + +//============================================================ + +void ThreatManager::setCurrentVictim(HostilReference* pHostilReference) +{ + iCurrentVictim = pHostilReference; +} + +//============================================================ +// The hated unit is gone, dead or deleted +// return true, if the event is consumed + +bool ThreatManager::processThreatEvent(const UnitBaseEvent* pUnitBaseEvent) +{ + bool consumed = false; + + ThreatRefStatusChangeEvent* threatRefStatusChangeEvent; + HostilReference* hostilReference; + + threatRefStatusChangeEvent = (ThreatRefStatusChangeEvent*) pUnitBaseEvent; + threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager + hostilReference = threatRefStatusChangeEvent->getReference(); + + switch(pUnitBaseEvent->getType()) + { + case UEV_THREAT_REF_THREAT_CHANGE: + if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) || + (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f)) + setDirty(true); // the order in the threat list might have changed + break; + case UEV_THREAT_REF_ONLINE_STATUS: + if(!hostilReference->isOnline()) + { + if (hostilReference == getCurrentVictim()) + { + setCurrentVictim(NULL); + setDirty(true); + } + iThreatContainer.remove(hostilReference); + iThreatOfflineContainer.addReference(hostilReference); + } + else + { + if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat())) + setDirty(true); + iThreatContainer.addReference(hostilReference); + iThreatOfflineContainer.remove(hostilReference); + } + break; + case UEV_THREAT_REF_REMOVE_FROM_LIST: + if (hostilReference == getCurrentVictim()) + { + setCurrentVictim(NULL); + setDirty(true); + } + if(hostilReference->isOnline()) + iThreatContainer.remove(hostilReference); + else + iThreatOfflineContainer.remove(hostilReference); + break; + } + return consumed; +} diff --git a/src/game/ThreatManager.h b/src/game/ThreatManager.h new file mode 100644 index 00000000000..ab0270645f7 --- /dev/null +++ b/src/game/ThreatManager.h @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _THREATMANAGER +#define _THREATMANAGER + +#include "Common.h" +#include "SharedDefines.h" +#include "Utilities/LinkedReference/Reference.h" +#include "UnitEvents.h" + +#include + +//============================================================== + +class Unit; +class Creature; +class ThreatManager; +struct SpellEntry; + +//============================================================== +// Class to calculate the real threat based + +class ThreatCalcHelper +{ + public: + static float calcThreat(Unit* pHatedUnit, Unit* pHatingUnit, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL); +}; + +//============================================================== + +class MANGOS_DLL_SPEC HostilReference : public Reference +{ + private: + float iThreat; + float iTempThreatModifyer; // used for taunt + uint64 iUnitGuid; + bool iOnline; + bool iAccessible; + private: + // Inform the source, that the status of that reference was changed + void fireStatusChanged(const ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent); + + Unit* getSourceUnit(); + public: + HostilReference(Unit* pUnit, ThreatManager *pThreatManager, float pThreat); + + //================================================= + void addThreat(float pMod); + + void setThreat(float pThreat) { addThreat(pThreat - getThreat()); } + + void addThreatPercent(int32 pPercent) { float tmpThreat = iThreat; tmpThreat = tmpThreat * (pPercent+100) / 100; addThreat(tmpThreat-iThreat); } + + float getThreat() const { return iThreat; } + + bool isOnline() const { return iOnline; } + + // The Unit might be in water and the creature can not enter the water, but has range attack + // in this case online = true, but accessable = false + bool isAccessable() const { return iAccessible; } + + // used for temporary setting a threat and reducting it later again. + // the threat modification is stored + void setTempThreat(float pThreat) { iTempThreatModifyer = pThreat - getThreat(); if(iTempThreatModifyer != 0.0f) addThreat(iTempThreatModifyer); } + + void resetTempThreat() + { + if(iTempThreatModifyer != 0.0f) + { + addThreat(-iTempThreatModifyer); iTempThreatModifyer = 0.0f; + } + } + + float getTempThreatModifyer() { return iTempThreatModifyer; } + + //================================================= + // check, if source can reach target and set the status + void updateOnlineStatus(); + + void setOnlineOfflineState(bool pIsOnline); + + void setAccessibleState(bool pIsAccessible); + //================================================= + + bool operator ==(const HostilReference& pHostilReference) const { return pHostilReference.getUnitGuid() == getUnitGuid(); } + + //================================================= + + uint64 getUnitGuid() const { return iUnitGuid; } + + //================================================= + // reference is not needed anymore. realy delete it ! + + void removeReference(); + + //================================================= + + HostilReference* next() { return ((HostilReference* ) Reference::next()); } + + //================================================= + + // Tell our refTo (target) object that we have a link + void targetObjectBuildLink(); + + // Tell our refTo (taget) object, that the link is cut + void targetObjectDestroyLink(); + + // Tell our refFrom (source) object, that the link is cut (Target destroyed) + void sourceObjectDestroyLink(); +}; + +//============================================================== +class ThreatManager; + +class MANGOS_DLL_SPEC ThreatContainer +{ + private: + std::list iThreatList; + bool iDirty; + protected: + friend class ThreatManager; + + void remove(HostilReference* pRef) { iThreatList.remove(pRef); } + void addReference(HostilReference* pHostilReference) { iThreatList.push_back(pHostilReference); } + void clearReferences(); + // Sort the list if necessary + void update(); + public: + ThreatContainer() { iDirty = false; } + ~ThreatContainer() { clearReferences(); } + + HostilReference* addThreat(Unit* pVictim, float pThreat); + + void modifyThreatPercent(Unit *pVictim, int32 percent); + + HostilReference* selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim); + + void setDirty(bool pDirty) { iDirty = pDirty; } + + bool isDirty() { return iDirty; } + + bool empty() { return(iThreatList.empty()); } + + HostilReference* getMostHated() { return iThreatList.empty() ? NULL : iThreatList.front(); } + + HostilReference* getReferenceByTarget(Unit* pVictim); + + std::list& getThreatList() { return iThreatList; } +}; + +//================================================= + +class MANGOS_DLL_SPEC ThreatManager +{ + private: + HostilReference* iCurrentVictim; + Unit* iOwner; + ThreatContainer iThreatContainer; + ThreatContainer iThreatOfflineContainer; + public: + explicit ThreatManager(Unit *pOwner); + + ~ThreatManager() { clearReferences(); } + + void clearReferences(); + + void addThreat(Unit* pVictim, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL); + void modifyThreatPercent(Unit *pVictim, int32 pPercent); + + float getThreat(Unit *pVictim, bool pAlsoSearchOfflineList = false); + + bool isThreatListEmpty() { return iThreatContainer.empty();} + + bool processThreatEvent(const UnitBaseEvent* pUnitBaseEvent); + + HostilReference* getCurrentVictim() { return iCurrentVictim; } + + Unit* getOwner() { return iOwner; } + + Unit* getHostilTarget(); + + void tauntApply(Unit* pTaunter); + void tauntFadeOut(Unit *pTaunter); + + void setCurrentVictim(HostilReference* pHostilReference); + + void setDirty(bool pDirty) { iThreatContainer.setDirty(pDirty); } + + // methods to access the lists from the outside to do sume dirty manipulation (scriping and such) + // I hope they are used as little as possible. + inline std::list& getThreatList() { return iThreatContainer.getThreatList(); } + inline std::list& getOfflieThreatList() { return iThreatOfflineContainer.getThreatList(); } + inline ThreatContainer& getOnlineContainer() { return iThreatContainer; } + inline ThreatContainer& getOfflineContainer() { return iThreatOfflineContainer; } +}; + +//================================================= +#endif diff --git a/src/game/Tools.h b/src/game/Tools.h new file mode 100644 index 00000000000..d9615b7df63 --- /dev/null +++ b/src/game/Tools.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef MANGOS_TOOLS_H +#define MANGOS_TOOLS_H + +#include "Common.h" +#include "WorldPacket.h" + +bool readGUID(WorldPacket & data, uint64& guid); +void writeGUID(WorldPacket & data, uint64 & guid); +#endif diff --git a/src/game/Totem.cpp b/src/game/Totem.cpp new file mode 100644 index 00000000000..5415e4dbe87 --- /dev/null +++ b/src/game/Totem.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Totem.h" +#include "WorldPacket.h" +#include "MapManager.h" +#include "Log.h" +#include "Group.h" +#include "Player.h" +#include "ObjectMgr.h" +#include "SpellMgr.h" + +Totem::Totem() : Creature() +{ + m_isTotem = true; + m_duration = 0; + m_type = TOTEM_PASSIVE; +} + +void Totem::Update( uint32 time ) +{ + Unit *owner = GetOwner(); + if (!owner || !owner->isAlive() || !this->isAlive()) + { + UnSummon(); // remove self + return; + } + + if (m_duration <= time) + { + UnSummon(); // remove self + return; + } + else + m_duration -= time; + + Creature::Update( time ); +} + +void Totem::Summon(Unit* owner) +{ + sLog.outDebug("AddObject at Totem.cpp line 49"); + + SetInstanceId(owner->GetInstanceId()); + owner->GetMap()->Add((Creature*)this); + + // select totem model in dependent from owner team + CreatureInfo const *cinfo = GetCreatureInfo(); + if(owner->GetTypeId()==TYPEID_PLAYER && cinfo) + { + if(((Player*)owner)->GetTeam()==HORDE) + SetDisplayId(cinfo->DisplayID_H); + else + SetDisplayId(cinfo->DisplayID_A); + } + + WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8); + data << GetGUID(); + SendMessageToSet(&data,true); + + AIM_Initialize(); + + switch(m_type) + { + case TOTEM_PASSIVE: CastSpell(this, GetSpell(), true); break; + case TOTEM_STATUE: CastSpell(GetOwner(), GetSpell(), true); break; + default: break; + } +} + +void Totem::UnSummon() +{ + SendObjectDeSpawnAnim(GetGUID()); + + CombatStop(); + RemoveAurasDueToSpell(GetSpell()); + Unit *owner = this->GetOwner(); + if (owner) + { + // clear owenr's totem slot + for(int i = 0; i < MAX_TOTEM; ++i) + { + if(owner->m_TotemSlot[i]==GetGUID()) + { + owner->m_TotemSlot[i] = 0; + break; + } + } + + owner->RemoveAurasDueToSpell(GetSpell()); + + //remove aura all party members too + Group *pGroup = NULL; + if (owner->GetTypeId() == TYPEID_PLAYER) + { + // Not only the player can summon the totem (scripted AI) + pGroup = ((Player*)owner)->GetGroup(); + if (pGroup) + { + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* Target = itr->getSource(); + if(Target && pGroup->SameSubGroup((Player*)owner, Target)) + Target->RemoveAurasDueToSpell(GetSpell()); + } + } + } + } + + CleanupsBeforeDelete(); + AddObjectToRemoveList(); +} + +void Totem::SetOwner(uint64 guid) +{ + SetUInt64Value(UNIT_FIELD_SUMMONEDBY, guid); + SetUInt64Value(UNIT_FIELD_CREATEDBY, guid); + Unit *owner = this->GetOwner(); + if (owner) + { + this->setFaction(owner->getFaction()); + this->SetLevel(owner->getLevel()); + } +} + +Unit *Totem::GetOwner() +{ + uint64 ownerid = GetOwnerGUID(); + if(!ownerid) + return NULL; + return ObjectAccessor::GetUnit(*this, ownerid); +} + +void Totem::SetTypeBySummonSpell(SpellEntry const * spellProto) +{ + // Get spell casted by totem + SpellEntry const * totemSpell = sSpellStore.LookupEntry(GetSpell()); + if (totemSpell) + { + // If spell have cast time -> so its active totem + if (GetSpellCastTime(totemSpell)) + m_type = TOTEM_ACTIVE; + } + if(spellProto->SpellIconID==2056) + m_type = TOTEM_STATUE; //Jewelery statue +} diff --git a/src/game/Totem.h b/src/game/Totem.h new file mode 100644 index 00000000000..34bf440c68e --- /dev/null +++ b/src/game/Totem.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_TOTEM_H +#define MANGOSSERVER_TOTEM_H + +#include "Creature.h" + +enum TotemType +{ + TOTEM_PASSIVE = 0, + TOTEM_ACTIVE = 1, + TOTEM_STATUE = 2 +}; + +class Totem : public Creature +{ + public: + explicit Totem(); + virtual ~Totem(){}; + void Update( uint32 time ); + void Summon(Unit* owner); + void UnSummon(); + uint32 GetSpell() const { return m_spells[0]; } + uint32 GetTotemDuration() const { return m_duration; } + Unit *GetOwner(); + TotemType GetTotemType() const { return m_type; } + void SetTypeBySummonSpell(SpellEntry const * spellProto); + void SetDuration(uint32 dur) { m_duration = dur; } + void SetOwner(uint64 guid); + + bool UpdateStats(Stats /*stat*/) { return true; } + bool UpdateAllStats() { return true; } + void UpdateResistances(uint32 /*school*/) {} + void UpdateArmor() {} + void UpdateMaxHealth() {} + void UpdateMaxPower(Powers /*power*/) {} + void UpdateAttackPowerAndDamage(bool /*ranged*/ ) {} + void UpdateDamagePhysical(WeaponAttackType /*attType*/) {} + + protected: + TotemType m_type; + uint32 m_duration; +}; +#endif diff --git a/src/game/TotemAI.cpp b/src/game/TotemAI.cpp new file mode 100644 index 00000000000..8fc038a8946 --- /dev/null +++ b/src/game/TotemAI.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "TotemAI.h" +#include "Totem.h" +#include "Creature.h" +#include "Player.h" +#include "Database/DBCStores.h" +#include "MapManager.h" +#include "ObjectAccessor.h" +#include "SpellMgr.h" + +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "CellImpl.h" + +int +TotemAI::Permissible(const Creature *creature) +{ + if( creature->isTotem() ) + return PERMIT_BASE_PROACTIVE; + + return PERMIT_BASE_NO; +} + +TotemAI::TotemAI(Creature &c) : i_totem(static_cast(c)), i_victimGuid(0) +{ +} + +void +TotemAI::MoveInLineOfSight(Unit *) +{ +} + +void TotemAI::EnterEvadeMode() +{ + i_totem.CombatStop(); +} + +void +TotemAI::UpdateAI(const uint32 /*diff*/) +{ + if (i_totem.GetTotemType() != TOTEM_ACTIVE) + return; + + if (!i_totem.isAlive() || i_totem.IsNonMeleeSpellCasted(false)) + return; + + // Search spell + SpellEntry const *spellInfo = sSpellStore.LookupEntry(i_totem.GetSpell()); + if (!spellInfo) + return; + + // Get spell rangy + SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex); + float max_range = GetSpellMaxRange(srange); + + // SPELLMOD_RANGE not applied in this place just because not existence range mods for attacking totems + + // pointer to appropriate target if found any + Unit* victim = i_victimGuid ? ObjectAccessor::GetUnit(i_totem, i_victimGuid) : NULL; + + // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) + if( !victim || + !victim->isTargetableForAttack() || !i_totem.IsWithinDistInMap(victim, max_range) || + i_totem.IsFriendlyTo(victim) || !victim->isVisibleForOrDetect(&i_totem,false) ) + { + CellPair p(MaNGOS::ComputeCellPair(i_totem.GetPositionX(),i_totem.GetPositionY())); + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + + victim = NULL; + + MaNGOS::NearestAttackableUnitInObjectRangeCheck u_check(&i_totem, &i_totem, max_range); + MaNGOS::UnitLastSearcher checker(victim, u_check); + + TypeContainerVisitor, GridTypeMapContainer > grid_object_checker(checker); + TypeContainerVisitor, WorldTypeMapContainer > world_object_checker(checker); + + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(i_totem.GetMapId(), &i_totem)); + cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(i_totem.GetMapId(), &i_totem)); + } + + // If have target + if (victim) + { + // remember + i_victimGuid = victim->GetGUID(); + + // attack + i_totem.SetInFront(victim); // client change orientation by self + i_totem.CastSpell(victim, i_totem.GetSpell(), false); + } + else + i_victimGuid = 0; +} + +bool +TotemAI::IsVisible(Unit *) const +{ + return false; +} + +void +TotemAI::AttackStart(Unit *) +{ +} diff --git a/src/game/TotemAI.h b/src/game/TotemAI.h new file mode 100644 index 00000000000..e113ac441a1 --- /dev/null +++ b/src/game/TotemAI.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_TOTEMAI_H +#define MANGOS_TOTEMAI_H + +#include "CreatureAI.h" +#include "Timer.h" + +class Creature; +class Totem; + +class MANGOS_DLL_DECL TotemAI : public CreatureAI +{ + public: + + TotemAI(Creature &c); + + void MoveInLineOfSight(Unit *); + void AttackStart(Unit *); + void EnterEvadeMode(); + bool IsVisible(Unit *) const; + + void UpdateAI(const uint32); + static int Permissible(const Creature *); + + private: + Totem &i_totem; + uint64 i_victimGuid; +}; +#endif diff --git a/src/game/TradeHandler.cpp b/src/game/TradeHandler.cpp new file mode 100644 index 00000000000..eb874f82ffb --- /dev/null +++ b/src/game/TradeHandler.cpp @@ -0,0 +1,633 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "ObjectAccessor.h" +#include "Log.h" +#include "Opcodes.h" +#include "Player.h" +#include "Item.h" +#include "SocialMgr.h" + +enum TradeStatus +{ + TRADE_STATUS_BUSY = 0, + TRADE_STATUS_BEGIN_TRADE = 1, + TRADE_STATUS_OPEN_WINDOW = 2, + TRADE_STATUS_TRADE_CANCELED = 3, + TRADE_STATUS_TRADE_ACCEPT = 4, + TRADE_STATUS_BUSY_2 = 5, + TRADE_STATUS_NO_TARGET = 6, + TRADE_STATUS_BACK_TO_TRADE = 7, + TRADE_STATUS_TRADE_COMPLETE = 8, + // 9? + TRADE_STATUS_TARGET_TO_FAR = 10, + TRADE_STATUS_WRONG_FACTION = 11, + TRADE_STATUS_CLOSE_WINDOW = 12, + // 13? + TRADE_STATUS_IGNORE_YOU = 14, + TRADE_STATUS_YOU_STUNNED = 15, + TRADE_STATUS_TARGET_STUNNED = 16, + TRADE_STATUS_YOU_DEAD = 17, + TRADE_STATUS_TARGET_DEAD = 18, + TRADE_STATUS_YOU_LOGOUT = 19, + TRADE_STATUS_TARGET_LOGOUT = 20, + TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action + TRADE_STATUS_ONLY_CONJURED = 22 // You can only trade conjured items... (cross realm BG related). +}; + +void WorldSession::SendTradeStatus(uint32 status) +{ + WorldPacket data; + + switch(status) + { + case TRADE_STATUS_BEGIN_TRADE: + data.Initialize(SMSG_TRADE_STATUS, 4+8); + data << uint32(status); + data << uint64(0); + break; + case TRADE_STATUS_OPEN_WINDOW: + data.Initialize(SMSG_TRADE_STATUS, 4+4); + data << uint32(status); + data << uint32(0); // added in 2.4.0 + break; + case TRADE_STATUS_CLOSE_WINDOW: + data.Initialize(SMSG_TRADE_STATUS, 4+4+1+4); + data << uint32(status); + data << uint32(0); + data << uint8(0); + data << uint32(0); + break; + case TRADE_STATUS_ONLY_CONJURED: + data.Initialize(SMSG_TRADE_STATUS, 4+1); + data << uint32(status); + data << uint8(0); + break; + default: + data.Initialize(SMSG_TRADE_STATUS, 4); + data << uint32(status); + break; + } + + SendPacket(&data); +} + +void WorldSession::HandleIgnoreTradeOpcode(WorldPacket& /*recvPacket*/) +{ + sLog.outDebug( "WORLD: Ignore Trade %u",_player->GetGUIDLow()); + // recvPacket.print_storage(); +} + +void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/) +{ + sLog.outDebug( "WORLD: Busy Trade %u",_player->GetGUIDLow()); + // recvPacket.print_storage(); +} + +void WorldSession::SendUpdateTrade() +{ + Item *item = NULL; + + if( !_player || !_player->pTrader ) + return; + + // reset trade status + if (_player->acceptTrade) + { + _player->acceptTrade = false; + SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + } + + if (_player->pTrader->acceptTrade) + { + _player->pTrader->acceptTrade = false; + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + } + + WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, (100)); // guess size + data << (uint8 ) 1; // can be different (only seen 0 and 1) + data << (uint32) 0; // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?) + data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = next field in most cases + data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = prev field in most cases + data << (uint32) _player->pTrader->tradeGold; // trader gold + data << (uint32) 0; // spell casted on lowest slot item + + for(uint8 i = 0; i < TRADE_SLOT_COUNT; i++) + { + item = (_player->pTrader->tradeItems[i] != NULL_SLOT ? _player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i] ) : NULL); + + data << (uint8) i; // trade slot number, if not specified, then end of packet + + if(item) + { + data << (uint32) item->GetProto()->ItemId; // entry + // display id + data << (uint32) item->GetProto()->DisplayInfoID; + // stack count + data << (uint32) item->GetUInt32Value(ITEM_FIELD_STACK_COUNT); + data << (uint32) 0; // probably gift=1, created_by=0? + // gift creator + data << (uint64) item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR); + data << (uint32) item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT); + for(uint8 j = 0; j < 3; ++j) + data << (uint32) 0; // enchantment id (permanent/gems?) + // creator + data << (uint64) item->GetUInt64Value(ITEM_FIELD_CREATOR); + data << (uint32) item->GetSpellCharges(); // charges + data << (uint32) item->GetItemSuffixFactor(); // SuffixFactor + // random properties id + data << (uint32) item->GetItemRandomPropertyId(); + data << (uint32) item->GetProto()->LockID; // lock id + // max durability + data << (uint32) item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); + // durability + data << (uint32) item->GetUInt32Value(ITEM_FIELD_DURABILITY); + } + else + { + for(uint8 j = 0; j < 18; j++) + data << uint32(0); + } + } + SendPacket(&data); +} + +//============================================================== +// transfer the items to the players + +void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) +{ + for(int i=0; ipTrader->CanStoreItem( NULL_BAG, NULL_SLOT, traderDst, myItems[i], false ) == EQUIP_ERR_OK); + bool playerCanTrade = (hisItems[i]==NULL || _player->CanStoreItem( NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false ) == EQUIP_ERR_OK); + if(traderCanTrade && playerCanTrade ) + { + // Ok, if trade item exists and can be stored + // If we trade in both directions we had to check, if the trade will work before we actually do it + // A roll back is not possible after we stored it + if(myItems[i]) + { + // logging + sLog.outDebug("partner storing: %u",myItems[i]->GetGUIDLow()); + if( _player->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) + sLog.outCommand("GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)", + _player->GetName(),_player->GetSession()->GetAccountId(), + myItems[i]->GetProto()->Name1,myItems[i]->GetEntry(),myItems[i]->GetCount(), + _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId()); + + // store + _player->pTrader->MoveItemToInventory( traderDst, myItems[i], true, true); + } + if(hisItems[i]) + { + // logging + sLog.outDebug("player storing: %u",hisItems[i]->GetGUIDLow()); + if( _player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) + sLog.outCommand("GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)", + _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(), + hisItems[i]->GetProto()->Name1,hisItems[i]->GetEntry(),hisItems[i]->GetCount(), + _player->GetName(),_player->GetSession()->GetAccountId()); + + // store + _player->MoveItemToInventory( playerDst, hisItems[i], true, true); + } + } + else + { + // in case of fatal error log error message + // return the already removed items to the original owner + if(myItems[i]) + { + if(!traderCanTrade) + sLog.outError("trader can't store item: %u",myItems[i]->GetGUIDLow()); + if(_player->CanStoreItem( NULL_BAG, NULL_SLOT, playerDst, myItems[i], false ) == EQUIP_ERR_OK) + _player->MoveItemToInventory(playerDst, myItems[i], true, true); + else + sLog.outError("player can't take item back: %u",myItems[i]->GetGUIDLow()); + } + // return the already removed items to the original owner + if(hisItems[i]) + { + if(!playerCanTrade) + sLog.outError("player can't store item: %u",hisItems[i]->GetGUIDLow()); + if(_player->pTrader->CanStoreItem( NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false ) == EQUIP_ERR_OK) + _player->pTrader->MoveItemToInventory(traderDst, hisItems[i], true, true); + else + sLog.outError("trader can't take item back: %u",hisItems[i]->GetGUIDLow()); + } + } + } +} + +//============================================================== + +void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) +{ + Item *myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; + Item *hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; + bool myCanCompleteTrade=true,hisCanCompleteTrade=true; + + if ( !GetPlayer()->pTrader ) + return; + + // not accept case incorrect money amount + if( _player->tradeGold > _player->GetMoney() ) + { + SendNotification( "You do not have enough gold" ); + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + _player->acceptTrade = false; + return; + } + + // not accept case incorrect money amount + if( _player->pTrader->tradeGold > _player->pTrader->GetMoney() ) + { + _player->pTrader->GetSession( )->SendNotification( "You do not have enough gold" ); + SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + _player->pTrader->acceptTrade = false; + return; + } + + // not accept if some items now can't be trade (cheating) + for(int i=0; itradeItems[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; itradeItems[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; iSetInTrade(false); + if(hisItems[i]) hisItems[i]->SetInTrade(false); + } + + // in case of missing space report error + if(!myCanCompleteTrade) + { + SendNotification("You do not have enough free slots"); + GetPlayer( )->pTrader->GetSession( )->SendNotification("Your partner does not have enough free bag slots"); + SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + return; + } + else if (!hisCanCompleteTrade) + { + SendNotification("Your partner does not have enough free bag slots"); + GetPlayer()->pTrader->GetSession()->SendNotification("You do not have enough free slots"); + SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + return; + } + + // execute trade: 1. remove + for(int i=0; iSetUInt64Value( ITEM_FIELD_GIFTCREATOR,_player->GetGUID()); + _player->MoveItemFromInventory(_player->tradeItems[i] >> 8, _player->tradeItems[i] & 255, true); + } + if(hisItems[i]) + { + hisItems[i]->SetUInt64Value( ITEM_FIELD_GIFTCREATOR,_player->pTrader->GetGUID()); + _player->pTrader->MoveItemFromInventory(_player->pTrader->tradeItems[i] >> 8, _player->pTrader->tradeItems[i] & 255, true); + } + } + + // execute trade: 2. store + moveItems(myItems, hisItems); + + // logging money + if(sWorld.getConfig(CONFIG_GM_LOG_TRADE)) + { + if( _player->GetSession()->GetSecurity() > SEC_PLAYER && _player->tradeGold > 0) + sLog.outCommand("GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", + _player->GetName(),_player->GetSession()->GetAccountId(), + _player->tradeGold, + _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId()); + if( _player->pTrader->GetSession()->GetSecurity() > SEC_PLAYER && _player->pTrader->tradeGold > 0) + sLog.outCommand("GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", + _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId(), + _player->pTrader->tradeGold, + _player->GetName(),_player->GetSession()->GetAccountId()); + } + + // update money + _player->ModifyMoney( -int32(_player->tradeGold) ); + _player->ModifyMoney(_player->pTrader->tradeGold ); + _player->pTrader->ModifyMoney( -int32(_player->pTrader->tradeGold) ); + _player->pTrader->ModifyMoney(_player->tradeGold ); + + _player->ClearTrade(); + _player->pTrader->ClearTrade(); + + // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards) + CharacterDatabase.BeginTransaction(); + _player->SaveInventoryAndGoldToDB(); + _player->pTrader->SaveInventoryAndGoldToDB(); + CharacterDatabase.CommitTransaction(); + + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); + SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); + + _player->pTrader->pTrader = NULL; + _player->pTrader = NULL; + } + else + { + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); + } +} + +void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/) +{ + if ( !GetPlayer()->pTrader ) + return; + + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + _player->acceptTrade = false; +} + +void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/) +{ + if(!_player->pTrader) + return; + + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); + _player->pTrader->ClearTrade(); + + SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); + _player->ClearTrade(); +} + +void WorldSession::SendCancelTrade() +{ + SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); +} + +void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/) +{ + // sended also after LOGOUT COMPLETE + if(_player) // needed because STATUS_AUTHED + _player->TradeCancel(true); +} + +void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,8); + + if( GetPlayer()->pTrader ) + return; + + uint64 ID; + + if( !GetPlayer()->isAlive() ) + { + SendTradeStatus(TRADE_STATUS_YOU_DEAD); + return; + } + + if( GetPlayer()->hasUnitState(UNIT_STAT_STUNDED) ) + { + SendTradeStatus(TRADE_STATUS_YOU_STUNNED); + return; + } + + if( isLogingOut() ) + { + SendTradeStatus(TRADE_STATUS_YOU_LOGOUT); + return; + } + + if( GetPlayer()->isInFlight() ) + { + SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); + return; + } + + recvPacket >> ID; + + Player* pOther = ObjectAccessor::FindPlayer( ID ); + + if( !pOther ) + { + SendTradeStatus(TRADE_STATUS_NO_TARGET); + return; + } + + if( pOther == GetPlayer() || pOther->pTrader ) + { + SendTradeStatus(TRADE_STATUS_BUSY); + return; + } + + if( !pOther->isAlive() ) + { + SendTradeStatus(TRADE_STATUS_TARGET_DEAD); + return; + } + + if( pOther->isInFlight() ) + { + SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); + return; + } + + if( pOther->hasUnitState(UNIT_STAT_STUNDED) ) + { + SendTradeStatus(TRADE_STATUS_TARGET_STUNNED); + return; + } + + if( pOther->GetSession()->isLogingOut() ) + { + SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT); + return; + } + + if( pOther->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()) ) + { + SendTradeStatus(TRADE_STATUS_IGNORE_YOU); + return; + } + + if(pOther->GetTeam() !=_player->GetTeam() ) + { + SendTradeStatus(TRADE_STATUS_WRONG_FACTION); + return; + } + + if( pOther->GetDistance2d( _player ) > 10.0f ) + { + SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); + return; + } + + // OK start trade + _player->pTrader = pOther; + pOther->pTrader =_player; + + WorldPacket data(SMSG_TRADE_STATUS, 12); + data << (uint32) TRADE_STATUS_BEGIN_TRADE; + data << (uint64)_player->GetGUID(); + _player->pTrader->GetSession()->SendPacket(&data); +} + +void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,4); + + if(!_player->pTrader) + return; + + uint32 gold; + + recvPacket >> gold; + + // gold can be incorrect, but this is checked at trade finished. + _player->tradeGold = gold; + + _player->pTrader->GetSession()->SendUpdateTrade(); +} + +void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,1+1+1); + + if(!_player->pTrader) + return; + + // send update + uint8 tradeSlot; + uint8 bag; + uint8 slot; + + recvPacket >> tradeSlot; + recvPacket >> bag; + recvPacket >> slot; + + // invalid slot number + if(tradeSlot >= TRADE_SLOT_COUNT) + { + SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + return; + } + + // check cheating, can't fail with correct client operations + Item* item = _player->GetItemByPos(bag,slot); + if(!item || tradeSlot!=TRADE_SLOT_NONTRADED && !item->CanBeTraded()) + { + SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + return; + } + + uint16 pos = (bag << 8) | slot; + + // prevent place single item into many trade slots using cheating and client bugs + for(int i = 0; i < TRADE_SLOT_COUNT; ++i) + { + if(_player->tradeItems[i]==pos) + { + // cheating attempt + SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + return; + } + } + + _player->tradeItems[tradeSlot] = pos; + + _player->pTrader->GetSession()->SendUpdateTrade(); +} + +void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket) +{ + CHECK_PACKET_SIZE(recvPacket,1); + + if(!_player->pTrader) + return; + + uint8 tradeSlot; + recvPacket >> tradeSlot; + + // invalid slot number + if(tradeSlot >= TRADE_SLOT_COUNT) + return; + + _player->tradeItems[tradeSlot] = NULL_SLOT; + + _player->pTrader->GetSession()->SendUpdateTrade(); +} diff --git a/src/game/Transports.cpp b/src/game/Transports.cpp new file mode 100644 index 00000000000..be5fa09606f --- /dev/null +++ b/src/game/Transports.cpp @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" + +#include "Transports.h" +#include "MapManager.h" +#include "ObjectMgr.h" +#include "Path.h" + +#include "WorldPacket.h" +#include "Database/DBCStores.h" +#include "ProgressBar.h" + +void MapManager::LoadTransports() +{ + QueryResult *result = WorldDatabase.Query("SELECT entry, name, period FROM transports"); + + uint32 count = 0; + + if( !result ) + { + barGoLink bar( 1 ); + bar.step(); + + sLog.outString(); + sLog.outString( ">> Loaded %u transports", count ); + return; + } + + barGoLink bar( result->GetRowCount() ); + + do + { + bar.step(); + + Transport *t = new Transport; + + Field *fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + std::string name = fields[1].GetCppString(); + t->m_period = fields[2].GetUInt32(); + + const GameObjectInfo *goinfo = objmgr.GetGameObjectInfo(entry); + + if(!goinfo) + { + sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template missing", entry, name.c_str()); + delete t; + continue; + } + + if(goinfo->type != GAMEOBJECT_TYPE_MO_TRANSPORT) + { + sLog.outErrorDb("Transport ID:%u, Name: %s, will not be loaded, gameobject_template type wrong", entry, name.c_str()); + delete t; + continue; + } + + // sLog.outString("Loading transport %d between %s, %s", entry, name.c_str(), goinfo->name); + + std::set mapsUsed; + + if(!t->GenerateWaypoints(goinfo->moTransport.taxiPathId, mapsUsed)) + // skip transports with empty waypoints list + { + sLog.outErrorDb("Transport (path id %u) path size = 0. Transport ignored, check DBC files or transport GO data0 field.",goinfo->moTransport.taxiPathId); + delete t; + continue; + } + + t->m_name = goinfo->name; + + float x, y, z, o; + uint32 mapid; + x = t->m_WayPoints[0].x; y = t->m_WayPoints[0].y; z = t->m_WayPoints[0].z; mapid = t->m_WayPoints[0].mapid; o = 1; + + // creates the Gameobject + if(!t->Create(entry, mapid, x, y, z, o, 100, 0)) + { + delete t; + continue; + } + + m_Transports.insert(t); + + for (std::set::iterator i = mapsUsed.begin(); i != mapsUsed.end(); ++i) + m_TransportsByMap[*i].insert(t); + + //If we someday decide to use the grid to track transports, here: + //MapManager::Instance().LoadGrid(mapid,x,y,true); + //MapManager::Instance().GetMap(t->GetMapId())->Add((GameObject *)t); + ++count; + } while(result->NextRow()); + delete result; + + sLog.outString(); + sLog.outString( ">> Loaded %u transports", count ); + + // check transport data DB integrity + result = WorldDatabase.PQuery("SELECT gameobject.guid,gameobject.id,transports.name FROM gameobject,transports WHERE gameobject.id = transports.entry"); + if(result) // wrong data found + { + do + { + Field *fields = result->Fetch(); + + uint32 guid = fields[0].GetUInt32(); + uint32 entry = fields[1].GetUInt32(); + std::string name = fields[2].GetCppString(); + sLog.outErrorDb("Transport %u '%s' have record (GUID: %u) in `gameobject`. Transports DON'T must have any records in `gameobject` or its behavior will be unpredictable/bugged.",entry,name.c_str(),guid); + } + while(result->NextRow()); + + delete result; + } +} + +Transport::Transport() : GameObject() +{ + // 2.3.2 - 0x5A + m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION); +} + +bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags) +{ + Relocate(x,y,z,ang); + + SetMapId(mapid); + + if(!IsPositionValid()) + { + sLog.outError("ERROR: Transport (GUID: %u) not created. Suggested coordinates isn't valid (X: %d Y: ^%d)",guidlow,x,y); + return false; + } + + Object::_Create(guidlow, 0, HIGHGUID_MO_TRANSPORT); + + GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(guidlow); + + if (!goinfo) + { + sLog.outErrorDb("Transport not created: entry in `gameobject_template` not found, guidlow: %u map: %u (X: %f Y: %f Z: %f) ang: %f",guidlow, mapid, x, y, z, ang); + return false; + } + + m_goInfo = goinfo; + + 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(1); + SetGoType(GameobjectTypes(goinfo->type)); + + SetGoAnimProgress(animprogress); + if(dynflags) + SetUInt32Value(GAMEOBJECT_DYN_FLAGS, dynflags); + + return true; +} + +struct keyFrame +{ + keyFrame(float _x, float _y, float _z, uint32 _mapid, int _actionflag, int _delay) + { + x = _x; y = _y; z = _z; mapid = _mapid; actionflag = _actionflag; delay = _delay; distFromPrev = -1; distSinceStop = -1; distUntilStop = -1; + tFrom = 0; tTo = 0; + } + + float x; + float y; + float z; + uint32 mapid; + int actionflag; + int delay; + float distSinceStop; + float distUntilStop; + float distFromPrev; + float tFrom, tTo; +}; + +bool Transport::GenerateWaypoints(uint32 pathid, std::set &mapids) +{ + TransportPath path; + objmgr.GetTransportPathNodes(pathid, path); + + if (path.Empty()) + return false; + + std::vector keyFrames; + int mapChange = 0; + mapids.clear(); + for (size_t i = 1; i < path.Size() - 1; i++) + { + if (mapChange == 0) + { + if ((path[i].mapid == path[i+1].mapid)) + { + keyFrame k(path[i].x, path[i].y, path[i].z, path[i].mapid, path[i].actionFlag, path[i].delay); + keyFrames.push_back(k); + mapids.insert(k.mapid); + } + else + { + mapChange = 1; + } + } + else + { + --mapChange; + } + } + + int lastStop = -1; + int firstStop = -1; + + // first cell is arrived at by teleportation :S + keyFrames[0].distFromPrev = 0; + if (keyFrames[0].actionflag == 2) + { + lastStop = 0; + } + + // find the rest of the distances between key points + for (size_t i = 1; i < keyFrames.size(); i++) + { + if ((keyFrames[i].actionflag == 1) || (keyFrames[i].mapid != keyFrames[i-1].mapid)) + { + keyFrames[i].distFromPrev = 0; + } + else + { + keyFrames[i].distFromPrev = + sqrt(pow(keyFrames[i].x - keyFrames[i - 1].x, 2) + + pow(keyFrames[i].y - keyFrames[i - 1].y, 2) + + pow(keyFrames[i].z - keyFrames[i - 1].z, 2)); + } + if (keyFrames[i].actionflag == 2) + { + // remember first stop frame + if(firstStop == -1) + firstStop = i; + lastStop = i; + } + } + + float tmpDist = 0; + for (size_t i = 0; i < keyFrames.size(); i++) + { + int j = (i + lastStop) % keyFrames.size(); + if (keyFrames[j].actionflag == 2) + tmpDist = 0; + else + tmpDist += keyFrames[j].distFromPrev; + keyFrames[j].distSinceStop = tmpDist; + } + + for (int i = int(keyFrames.size()) - 1; i >= 0; i--) + { + int j = (i + (firstStop+1)) % keyFrames.size(); + tmpDist += keyFrames[(j + 1) % keyFrames.size()].distFromPrev; + keyFrames[j].distUntilStop = tmpDist; + if (keyFrames[j].actionflag == 2) + tmpDist = 0; + } + + for (size_t i = 0; i < keyFrames.size(); i++) + { + if (keyFrames[i].distSinceStop < (30 * 30 * 0.5f)) + keyFrames[i].tFrom = sqrt(2 * keyFrames[i].distSinceStop); + else + keyFrames[i].tFrom = ((keyFrames[i].distSinceStop - (30 * 30 * 0.5f)) / 30) + 30; + + if (keyFrames[i].distUntilStop < (30 * 30 * 0.5f)) + keyFrames[i].tTo = sqrt(2 * keyFrames[i].distUntilStop); + else + keyFrames[i].tTo = ((keyFrames[i].distUntilStop - (30 * 30 * 0.5f)) / 30) + 30; + + keyFrames[i].tFrom *= 1000; + keyFrames[i].tTo *= 1000; + } + + // for (int i = 0; i < keyFrames.size(); i++) { + // sLog.outString("%f, %f, %f, %f, %f, %f, %f", keyFrames[i].x, keyFrames[i].y, keyFrames[i].distUntilStop, keyFrames[i].distSinceStop, keyFrames[i].distFromPrev, keyFrames[i].tFrom, keyFrames[i].tTo); + // } + + // Now we're completely set up; we can move along the length of each waypoint at 100 ms intervals + // speed = max(30, t) (remember x = 0.5s^2, and when accelerating, a = 1 unit/s^2 + int t = 0; + bool teleport = false; + if (keyFrames[keyFrames.size() - 1].mapid != keyFrames[0].mapid) + teleport = true; + + WayPoint pos(keyFrames[0].mapid, keyFrames[0].x, keyFrames[0].y, keyFrames[0].z, teleport); + m_WayPoints[0] = pos; + t += keyFrames[0].delay * 1000; + + uint32 cM = keyFrames[0].mapid; + for (size_t i = 0; i < keyFrames.size() - 1; i++) + { + float d = 0; + float tFrom = keyFrames[i].tFrom; + float tTo = keyFrames[i].tTo; + + // keep the generation of all these points; we use only a few now, but may need the others later + if (((d < keyFrames[i + 1].distFromPrev) && (tTo > 0))) + { + while ((d < keyFrames[i + 1].distFromPrev) && (tTo > 0)) + { + tFrom += 100; + tTo -= 100; + + if (d > 0) + { + float newX, newY, newZ; + newX = keyFrames[i].x + (keyFrames[i + 1].x - keyFrames[i].x) * d / keyFrames[i + 1].distFromPrev; + newY = keyFrames[i].y + (keyFrames[i + 1].y - keyFrames[i].y) * d / keyFrames[i + 1].distFromPrev; + newZ = keyFrames[i].z + (keyFrames[i + 1].z - keyFrames[i].z) * d / keyFrames[i + 1].distFromPrev; + + bool teleport = false; + if (keyFrames[i].mapid != cM) + { + teleport = true; + cM = keyFrames[i].mapid; + } + + // sLog.outString("T: %d, D: %f, x: %f, y: %f, z: %f", t, d, newX, newY, newZ); + WayPoint pos(keyFrames[i].mapid, newX, newY, newZ, teleport); + if (teleport) + m_WayPoints[t] = pos; + } + + if (tFrom < tTo) // caught in tFrom dock's "gravitational pull" + { + if (tFrom <= 30000) + { + d = 0.5f * (tFrom / 1000) * (tFrom / 1000); + } + else + { + d = 0.5f * 30 * 30 + 30 * ((tFrom - 30000) / 1000); + } + d = d - keyFrames[i].distSinceStop; + } + else + { + if (tTo <= 30000) + { + d = 0.5f * (tTo / 1000) * (tTo / 1000); + } + else + { + d = 0.5f * 30 * 30 + 30 * ((tTo - 30000) / 1000); + } + d = keyFrames[i].distUntilStop - d; + } + t += 100; + } + t -= 100; + } + + if (keyFrames[i + 1].tFrom > keyFrames[i + 1].tTo) + t += 100 - ((long)keyFrames[i + 1].tTo % 100); + else + t += (long)keyFrames[i + 1].tTo % 100; + + bool teleport = false; + if ((keyFrames[i + 1].actionflag == 1) || (keyFrames[i + 1].mapid != keyFrames[i].mapid)) + { + teleport = true; + cM = keyFrames[i + 1].mapid; + } + + WayPoint pos(keyFrames[i + 1].mapid, keyFrames[i + 1].x, keyFrames[i + 1].y, keyFrames[i + 1].z, teleport); + + // sLog.outString("T: %d, x: %f, y: %f, z: %f, t:%d", t, pos.x, pos.y, pos.z, teleport); + + //if (teleport) + m_WayPoints[t] = pos; + + t += keyFrames[i + 1].delay * 1000; + // sLog.outString("------"); + } + + uint32 timer = t; + + // sLog.outDetail(" Generated %d waypoints, total time %u.", m_WayPoints.size(), timer); + + m_curr = m_WayPoints.begin(); + m_curr = GetNextWayPoint(); + m_next = GetNextWayPoint(); + m_pathTime = timer; + + m_nextNodeTime = m_curr->first; + + return true; +} + +Transport::WayPointMap::iterator Transport::GetNextWayPoint() +{ + WayPointMap::iterator iter = m_curr; + ++iter; + if (iter == m_WayPoints.end()) + iter = m_WayPoints.begin(); + return iter; +} + +void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z) +{ + //MapManager::Instance().GetMap(oldMapid)->Remove((GameObject *)this, false); + SetMapId(newMapid); + //MapManager::Instance().LoadGrid(newMapid,x,y,true); + Relocate(x, y, z); + //MapManager::Instance().GetMap(newMapid)->Add((GameObject *)this); + + for(PlayerSet::iterator itr = m_passengers.begin(); itr != m_passengers.end();) + { + PlayerSet::iterator it2 = itr; + ++itr; + + Player *plr = *it2; + if(!plr) + { + m_passengers.erase(it2); + continue; + } + + if (plr->isDead() && !plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) + { + plr->ResurrectPlayer(1.0); + } + plr->TeleportTo(newMapid, x, y, z, GetOrientation(), TELE_TO_NOT_LEAVE_TRANSPORT); + + //WorldPacket data(SMSG_811, 4); + //data << uint32(0); + //plr->GetSession()->SendPacket(&data); + } +} + +bool Transport::AddPassenger(Player* passenger) +{ + if (m_passengers.find(passenger) == m_passengers.end()) + { + sLog.outDetail("Player %s boarded transport %s.", passenger->GetName(), this->m_name.c_str()); + m_passengers.insert(passenger); + } + return true; +} + +bool Transport::RemovePassenger(Player* passenger) +{ + if (m_passengers.find(passenger) != m_passengers.end()) + { + sLog.outDetail("Player %s removed from transport %s.", passenger->GetName(), this->m_name.c_str()); + m_passengers.erase(passenger); + } + return true; +} + +void Transport::Update(uint32 /*p_time*/) +{ + if (m_WayPoints.size() <= 1) + return; + + m_timer = getMSTime() % m_period; + while (((m_timer - m_curr->first) % m_pathTime) > ((m_next->first - m_curr->first) % m_pathTime)) + { + m_curr = GetNextWayPoint(); + m_next = GetNextWayPoint(); + + // first check help in case client-server transport coordinates de-synchronization + if (m_curr->second.mapid != GetMapId() || m_curr->second.teleport) + { + TeleportTransport(m_curr->second.mapid, m_curr->second.x, m_curr->second.y, m_curr->second.z); + } + else + { + //MapManager::Instance().GetMap(m_curr->second.mapid)->GameobjectRelocation((GameObject *)this, m_curr->second.x, m_curr->second.y, m_curr->second.z, this->m_orientation); + Relocate(m_curr->second.x, m_curr->second.y, m_curr->second.z); + } + + /* + for(PlayerSet::iterator itr = m_passengers.begin(); itr != m_passengers.end();) + { + PlayerSet::iterator it2 = itr; + ++itr; + //(*it2)->SetPosition( m_curr->second.x + (*it2)->GetTransOffsetX(), m_curr->second.y + (*it2)->GetTransOffsetY(), m_curr->second.z + (*it2)->GetTransOffsetZ(), (*it2)->GetTransOffsetO() ); + } + */ + + m_nextNodeTime = m_curr->first; + + if (m_curr == m_WayPoints.begin() && (sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES)==0) + sLog.outDetail(" ************ BEGIN ************** %s", this->m_name.c_str()); + + // MapManager::Instance().GetMap(m_curr->second.mapid)->Add(&this); // -> // ->Add(t); + //MapManager::Instance().GetMap(m_curr->second.mapid)->Remove((GameObject *)this, false); // -> // ->Add(t); + //MapManager::Instance().GetMap(m_curr->second.mapid)->Add((GameObject *)this); // -> // ->Add(t); + + if ((sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES)==0) + sLog.outDetail("%s moved to %f %f %f %d", this->m_name.c_str(), m_curr->second.x, m_curr->second.y, m_curr->second.z, m_curr->second.mapid); + } +} diff --git a/src/game/Transports.h b/src/game/Transports.h new file mode 100644 index 00000000000..e104012c850 --- /dev/null +++ b/src/game/Transports.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRANSPORTS_H +#define TRANSPORTS_H + +#include "GameObject.h" + +#include +#include +#include + +class TransportPath +{ + public: + struct PathNode + { + uint32 mapid; + float x,y,z; + uint32 actionFlag; + uint32 delay; + }; + + inline void SetLength(const unsigned int sz) + { + i_nodes.resize( sz ); + } + + inline unsigned int Size(void) const { return i_nodes.size(); } + inline bool Empty(void) const { return i_nodes.empty(); } + inline void Resize(unsigned int sz) { i_nodes.resize(sz); } + inline void Clear(void) { i_nodes.clear(); } + inline PathNode* GetNodes(void) { return static_cast(&i_nodes[0]); } + + PathNode& operator[](const unsigned int idx) { return i_nodes[idx]; } + const PathNode& operator()(const unsigned int idx) const { return i_nodes[idx]; } + + protected: + std::vector i_nodes; +}; + +class Transport : private GameObject +{ + public: + explicit Transport(); + + // prevent using Transports as normal GO, but allow call some inherited functions + using GameObject::IsTransport; + using GameObject::GetEntry; + using GameObject::GetGUID; + using GameObject::GetGUIDLow; + using GameObject::GetMapId; + using GameObject::GetPositionX; + using GameObject::GetPositionY; + using GameObject::GetPositionZ; + using GameObject::BuildCreateUpdateBlockForPlayer; + using GameObject::BuildOutOfRangeUpdateBlock; + + bool Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags); + bool GenerateWaypoints(uint32 pathid, std::set &mapids); + void Update(uint32 p_time); + bool AddPassenger(Player* passenger); + bool RemovePassenger(Player* passenger); + + typedef std::set PlayerSet; + PlayerSet const& GetPassengers() const { return m_passengers; } + + std::string m_name; + private: + struct WayPoint + { + WayPoint() : mapid(0), x(0), y(0), z(0), teleport(false) {} + WayPoint(uint32 _mapid, float _x, float _y, float _z, bool _teleport) : + mapid(_mapid), x(_x), y(_y), z(_z), teleport(_teleport) {} + uint32 mapid; + float x; + float y; + float z; + bool teleport; + }; + + typedef std::map WayPointMap; + + WayPointMap::iterator m_curr; + WayPointMap::iterator m_next; + uint32 m_pathTime; + uint32 m_timer; + + PlayerSet m_passengers; + + public: + WayPointMap m_WayPoints; + uint32 m_nextNodeTime; + uint32 m_period; + + private: + void TeleportTransport(uint32 newMapid, float x, float y, float z); + WayPointMap::iterator GetNextWayPoint(); +}; +#endif diff --git a/src/game/Traveller.h b/src/game/Traveller.h new file mode 100644 index 00000000000..07a02bb3056 --- /dev/null +++ b/src/game/Traveller.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_TRAVELLER_H +#define MANGOS_TRAVELLER_H + +#include "MapManager.h" +#include "Creature.h" +#include "Player.h" +#include + +/** Traveller is a wrapper for units (creatures or players) that + * travel from point A to point B using the destination holder. + */ +#define PLAYER_FLIGHT_SPEED 32.0f + +template +struct MANGOS_DLL_DECL Traveller +{ + T &i_traveller; + Traveller(T &t) : i_traveller(t) {} + Traveller(const Traveller &obj) : i_traveller(obj) {} + Traveller& operator=(const Traveller &obj) + { + this->~Traveller(); + new (this) Traveller(obj); + return *this; + } + + operator T&(void) { return i_traveller; } + operator const T&(void) { return i_traveller; } + inline float GetPositionX() const { return i_traveller.GetPositionX(); } + inline float GetPositionY() const { return i_traveller.GetPositionY(); } + inline float GetPositionZ() const { return i_traveller.GetPositionZ(); } + inline T& GetTraveller(void) { return i_traveller; } + + float Speed(void) { assert(false); return 0.0f; } + void Relocation(float x, float y, float z, float orientation) {} + void Relocation(float x, float y, float z) { Relocation(x, y, z, i_traveller.GetOrientation()); } + void MoveTo(float x, float y, float z, uint32 t) {} +}; + +// specialization for creatures +template<> +inline float Traveller::Speed() +{ + return i_traveller.GetSpeed( i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN); +} + +template<> +inline void Traveller::Relocation(float x, float y, float z, float orientation) +{ + MapManager::Instance().GetMap(i_traveller.GetMapId(), &i_traveller)->CreatureRelocation(&i_traveller, x, y, z, orientation); +} + +template<> +inline void Traveller::MoveTo(float x, float y, float z, uint32 t) +{ + i_traveller.AI_SendMoveToPacket(x, y, z, t, i_traveller.GetUnitMovementFlags(), 0); +} + +// specialization for players +template<> +inline float Traveller::Speed() +{ + if (i_traveller.isInFlight()) + return PLAYER_FLIGHT_SPEED; + else + return i_traveller.GetSpeed(i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN); +} + +template<> +inline void Traveller::Relocation(float x, float y, float z, float orientation) +{ + MapManager::Instance().GetMap(i_traveller.GetMapId(), &i_traveller)->PlayerRelocation(&i_traveller, x, y, z, orientation); +} + +template<> +inline void Traveller::MoveTo(float x, float y, float z, uint32 t) +{ + //Only send MOVEMENTFLAG_WALK_MODE, client has strange issues with other move flags + i_traveller.SendMonsterMove(x, y, z, 0, MOVEMENTFLAG_WALK_MODE, t); +} + +typedef Traveller CreatureTraveller; +typedef Traveller PlayerTraveller; +#endif diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp new file mode 100644 index 00000000000..442dbc1057b --- /dev/null +++ b/src/game/Unit.cpp @@ -0,0 +1,10808 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "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 + +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(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_STUNDED)) + 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) + { + // battleground things + 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 + } + + 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); + } + } + 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 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_STUNDED | 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_STUNDED)) + 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_STUNDED)) + return 0.0f; + + float chance = 0.0f; + + if(GetTypeId() == TYPEID_PLAYER) + { + Player const* player = (Player const*)this; + if(player->CanParry() ) + { + Item *tmpitem = ((Player*)this)->GetWeaponForAttack(BASE_ATTACK,true); + if(!tmpitem) + tmpitem = ((Player*)this)->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_STUNDED)) + return 0.0f; + + if(GetTypeId() == TYPEID_PLAYER) + { + Item *tmpitem = ((Player const*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + if(tmpitem && !tmpitem->IsBroken() && tmpitem->GetProto()->Block) + return GetFloatValue(PLAYER_BLOCK_PERCENTAGE); + else + 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::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(); +} + +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<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::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::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::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(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(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_STUNDED) ) + 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_STUNDED) && (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_STUNDED)) + 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 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<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<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(" "); + 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 targets; + + { + MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, this, ATTACK_DISTANCE); + MaNGOS::UnitListSearcher searcher(targets, u_check); + + TypeContainerVisitor, WorldTypeMapContainer > world_unit_searcher(searcher); + TypeContainerVisitor, GridTypeMapContainer > grid_unit_searcher(searcher); + + CellLock 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::iterator tIter = targets.begin(); tIter != targets.end();) + { + if(!IsWithinLOSInMap(*tIter)) + { + std::list::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::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 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 new file mode 100644 index 00000000000..b16b5e9ab2d --- /dev/null +++ b/src/game/Unit.h @@ -0,0 +1,1331 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __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 + +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 + +#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 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_STUNDED = 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 AttackerSet; + typedef std::pair spellEffectPair; + typedef std::multimap< spellEffectPair, Aura*> AuraMap; + typedef std::list AuraList; + typedef std::list Diminishing; + typedef std::set AuraTypeSet; + typedef std::set 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_STUNDED | 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 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 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 m_scAuras; // casted singlecast auras + + typedef std::list DynObjectGUIDs; + DynObjectGUIDs m_dynObjGUIDs; + + std::list 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/UnitEvents.h b/src/game/UnitEvents.h new file mode 100644 index 00000000000..4ff99535035 --- /dev/null +++ b/src/game/UnitEvents.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _UNITEVENTS +#define _UNITEVENTS + +#include "Common.h" + +class ThreatContainer; +class ThreatManager; +class HostilReference; + +//============================================================== +//============================================================== + +enum UNIT_EVENT_TYPE +{ + // Player/Pet changed on/offline status + UEV_THREAT_REF_ONLINE_STATUS = 1<<0, + + // Threat for Player/Pet changed + UEV_THREAT_REF_THREAT_CHANGE = 1<<1, + + // Player/Pet will be removed from list (dead) [for internal use] + UEV_THREAT_REF_REMOVE_FROM_LIST = 1<<2, + + // Player/Pet entered/left water or some other place where it is/was not accessible for the creature + UEV_THREAT_REF_ASSECCIBLE_STATUS = 1<<3, + + // Threat list is going to be sorted (if dirty flag is set) + UEV_THREAT_SORT_LIST = 1<<4, + + // New target should be fetched, could tbe the current target as well + UEV_THREAT_SET_NEXT_TARGET = 1<<5, + + // A new victim (target) was set. Could be NULL + UEV_THREAT_VICTIM_CHANGED = 1<<6, + + // Future use + //UEV_UNIT_KILLED = 1<<7, + + //Future use + //UEV_UNIT_HEALTH_CHANGE = 1<<8, +}; + +#define UEV_THREAT_REF_EVENT_MASK ( UEV_THREAT_REF_ONLINE_STATUS | UEV_THREAT_REF_THREAT_CHANGE | UEV_THREAT_REF_REMOVE_FROM_LIST | UEV_THREAT_REF_ASSECCIBLE_STATUS) +#define UEV_THREAT_MANAGER_EVENT_MASK (UEV_THREAT_SORT_LIST | UEV_THREAT_SET_NEXT_TARGET | UEV_THREAT_VICTIM_CHANGED) +#define UEV_ALL_EVENT_MASK (0xffffffff) + +// Future use +//#define UEV_UNIT_EVENT_MASK (UEV_UNIT_KILLED | UEV_UNIT_HEALTH_CHANGE) + +//============================================================== + +class MANGOS_DLL_SPEC UnitBaseEvent +{ + private: + uint32 iType; + public: + UnitBaseEvent(uint32 pType) { iType = pType; } + uint32 getType() const { return iType; } + bool matchesTypeMask(uint32 pMask) const { return iType & pMask; } + + void setType(uint32 pType) { iType = pType; } + +}; + +//============================================================== + +class MANGOS_DLL_SPEC ThreatRefStatusChangeEvent : public UnitBaseEvent +{ + private: + HostilReference* iHostilReference; + union + { + float iFValue; + int32 iIValue; + bool iBValue; + }; + ThreatManager* iThreatManager; + public: + ThreatRefStatusChangeEvent(uint32 pType) : UnitBaseEvent(pType) { iHostilReference = NULL; } + + ThreatRefStatusChangeEvent(uint32 pType, HostilReference* pHostilReference) : UnitBaseEvent(pType) { iHostilReference = pHostilReference; } + + ThreatRefStatusChangeEvent(uint32 pType, HostilReference* pHostilReference, float pValue) : UnitBaseEvent(pType) { iHostilReference = pHostilReference; iFValue = pValue; } + + ThreatRefStatusChangeEvent(uint32 pType, HostilReference* pHostilReference, bool pValue) : UnitBaseEvent(pType) { iHostilReference = pHostilReference; iBValue = pValue; } + + int32 getIValue() const { return iIValue; } + + float getFValue() const { return iFValue; } + + bool getBValue() const { return iBValue; } + + void setBValue(bool pValue) { iBValue = pValue; } + + HostilReference* getReference() const { return iHostilReference; } + + void setThreatManager(ThreatManager* pThreatManager) { iThreatManager = pThreatManager; } + + ThreatManager* getThreatManager() const { return iThreatManager; } +}; + +//============================================================== + +class MANGOS_DLL_SPEC ThreatManagerEvent : public ThreatRefStatusChangeEvent +{ + private: + ThreatContainer* iThreatContainer; + public: + ThreatManagerEvent(uint32 pType) : ThreatRefStatusChangeEvent(pType) {} + ThreatManagerEvent(uint32 pType, HostilReference* pHostilReference) : ThreatRefStatusChangeEvent(pType, pHostilReference) {} + + void setThreatContainer(ThreatContainer* pThreatContainer) { iThreatContainer = pThreatContainer; } + + ThreatContainer* getThreatContainer() const { return iThreatContainer; } +}; + +//============================================================== +#endif diff --git a/src/game/UpdateData.cpp b/src/game/UpdateData.cpp new file mode 100644 index 00000000000..eded8cb7395 --- /dev/null +++ b/src/game/UpdateData.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "ByteBuffer.h" +#include "WorldPacket.h" +#include "UpdateData.h" +#include "Log.h" +#include "Opcodes.h" +#include "World.h" +#include + +UpdateData::UpdateData() : m_blockCount(0) +{ +} + +void UpdateData::AddOutOfRangeGUID(std::set& guids) +{ + m_outOfRangeGUIDs.insert(guids.begin(),guids.end()); +} + +void UpdateData::AddOutOfRangeGUID(const uint64 &guid) +{ + m_outOfRangeGUIDs.insert(guid); +} + +void UpdateData::AddUpdateBlock(const ByteBuffer &block) +{ + m_data.append(block); + ++m_blockCount; +} + +void UpdateData::Compress(void* dst, uint32 *dst_size, void* src, int src_size) +{ + z_stream c_stream; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + // default Z_BEST_SPEED (1) + int z_res = deflateInit(&c_stream, sWorld.getConfig(CONFIG_COMPRESSION)); + if (z_res != Z_OK) + { + sLog.outError("Can't compress update packet (zlib: deflateInit) Error code: %i (%s)",z_res,zError(z_res)); + *dst_size = 0; + return; + } + + c_stream.next_out = (Bytef*)dst; + c_stream.avail_out = *dst_size; + c_stream.next_in = (Bytef*)src; + c_stream.avail_in = (uInt)src_size; + + z_res = deflate(&c_stream, Z_NO_FLUSH); + if (z_res != Z_OK) + { + sLog.outError("Can't compress update packet (zlib: deflate) Error code: %i (%s)",z_res,zError(z_res)); + *dst_size = 0; + return; + } + + if (c_stream.avail_in != 0) + { + sLog.outError("Can't compress update packet (zlib: deflate not greedy)"); + *dst_size = 0; + return; + } + + z_res = deflate(&c_stream, Z_FINISH); + if (z_res != Z_STREAM_END) + { + sLog.outError("Can't compress update packet (zlib: deflate should report Z_STREAM_END instead %i (%s)",z_res,zError(z_res)); + *dst_size = 0; + return; + } + + z_res = deflateEnd(&c_stream); + if (z_res != Z_OK) + { + sLog.outError("Can't compress update packet (zlib: deflateEnd) Error code: %i (%s)",z_res,zError(z_res)); + *dst_size = 0; + return; + } + + *dst_size = c_stream.total_out; +} + +bool UpdateData::BuildPacket(WorldPacket *packet, bool hasTransport) +{ + ByteBuffer buf(m_data.size() + 10 + m_outOfRangeGUIDs.size()*8); + + buf << (uint32) (!m_outOfRangeGUIDs.empty() ? m_blockCount + 1 : m_blockCount); + buf << (uint8) (hasTransport ? 1 : 0); + + if(!m_outOfRangeGUIDs.empty()) + { + buf << (uint8) UPDATETYPE_OUT_OF_RANGE_OBJECTS; + buf << (uint32) m_outOfRangeGUIDs.size(); + + for(std::set::const_iterator i = m_outOfRangeGUIDs.begin(); + i != m_outOfRangeGUIDs.end(); i++) + { + //buf.appendPackGUID(*i); + buf << (uint8)0xFF; + buf << (uint64) *i; + } + } + + buf.append(m_data); + + packet->clear(); + + if (m_data.size() > 50 ) + { + uint32 destsize = buf.size() + buf.size()/10 + 16; + packet->resize( destsize ); + + packet->put(0, (uint32)buf.size()); + + Compress(const_cast(packet->contents()) + sizeof(uint32), + &destsize, + (void*)buf.contents(), + buf.size()); + if (destsize == 0) + return false; + + packet->resize( destsize + sizeof(uint32) ); + packet->SetOpcode( SMSG_COMPRESSED_UPDATE_OBJECT ); + } + else + { + packet->append( buf ); + packet->SetOpcode( SMSG_UPDATE_OBJECT ); + } + + return true; +} + +void UpdateData::Clear() +{ + m_data.clear(); + m_outOfRangeGUIDs.clear(); + m_blockCount = 0; +} diff --git a/src/game/UpdateData.h b/src/game/UpdateData.h new file mode 100644 index 00000000000..20f5749d507 --- /dev/null +++ b/src/game/UpdateData.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __UPDATEDATA_H +#define __UPDATEDATA_H + +class WorldPacket; + +enum OBJECT_UPDATE_TYPE +{ + UPDATETYPE_VALUES = 0, + UPDATETYPE_MOVEMENT = 1, + UPDATETYPE_CREATE_OBJECT = 2, + UPDATETYPE_CREATE_OBJECT2 = 3, + UPDATETYPE_OUT_OF_RANGE_OBJECTS = 4, + UPDATETYPE_NEAR_OBJECTS = 5 +}; + +enum OBJECT_UPDATE_FLAGS +{ + UPDATEFLAG_NONE = 0x00, + UPDATEFLAG_SELF = 0x01, + UPDATEFLAG_TRANSPORT = 0x02, + UPDATEFLAG_FULLGUID = 0x04, + UPDATEFLAG_LOWGUID = 0x08, + UPDATEFLAG_HIGHGUID = 0x10, + UPDATEFLAG_LIVING = 0x20, + UPDATEFLAG_HASPOSITION = 0x40 +}; + +class UpdateData +{ + public: + UpdateData(); + + void AddOutOfRangeGUID(std::set& guids); + void AddOutOfRangeGUID(const uint64 &guid); + void AddUpdateBlock(const ByteBuffer &block); + bool BuildPacket(WorldPacket *packet, bool hasTransport = false); + bool HasData() { return m_blockCount > 0 || !m_outOfRangeGUIDs.empty(); } + void Clear(); + + std::set const& GetOutOfRangeGUIDs() const { return m_outOfRangeGUIDs; } + + protected: + uint32 m_blockCount; + std::set m_outOfRangeGUIDs; + ByteBuffer m_data; + + void Compress(void* dst, uint32 *dst_size, void* src, int src_size); +}; +#endif diff --git a/src/game/UpdateFields.h b/src/game/UpdateFields.h new file mode 100644 index 00000000000..5cff686d209 --- /dev/null +++ b/src/game/UpdateFields.h @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _UPDATEFIELDS_AUTO_H +#define _UPDATEFIELDS_AUTO_H + +// Auto generated for version 2, 4, 3, 8606 + +enum EObjectFields +{ + OBJECT_FIELD_GUID = 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC + OBJECT_FIELD_TYPE = 0x0002, // Size: 1, Type: INT, Flags: PUBLIC + OBJECT_FIELD_ENTRY = 0x0003, // Size: 1, Type: INT, Flags: PUBLIC + OBJECT_FIELD_SCALE_X = 0x0004, // Size: 1, Type: FLOAT, Flags: PUBLIC + OBJECT_FIELD_PADDING = 0x0005, // Size: 1, Type: INT, Flags: NONE + OBJECT_END = 0x0006, +}; + +enum EItemFields +{ + ITEM_FIELD_OWNER = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC + ITEM_FIELD_CONTAINED = OBJECT_END + 0x0002, // Size: 2, Type: LONG, Flags: PUBLIC + ITEM_FIELD_CREATOR = OBJECT_END + 0x0004, // Size: 2, Type: LONG, Flags: PUBLIC + ITEM_FIELD_GIFTCREATOR = OBJECT_END + 0x0006, // Size: 2, Type: LONG, Flags: PUBLIC + ITEM_FIELD_STACK_COUNT = OBJECT_END + 0x0008, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2 + ITEM_FIELD_DURATION = OBJECT_END + 0x0009, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2 + ITEM_FIELD_SPELL_CHARGES = OBJECT_END + 0x000A, // Size: 5, Type: INT, Flags: OWNER_ONLY, UNK2 + ITEM_FIELD_FLAGS = OBJECT_END + 0x000F, // Size: 1, Type: INT, Flags: PUBLIC + ITEM_FIELD_ENCHANTMENT = OBJECT_END + 0x0010, // Size: 33, Type: INT, Flags: PUBLIC + ITEM_FIELD_PROPERTY_SEED = OBJECT_END + 0x0031, // Size: 1, Type: INT, Flags: PUBLIC + ITEM_FIELD_RANDOM_PROPERTIES_ID = OBJECT_END + 0x0032, // Size: 1, Type: INT, Flags: PUBLIC + ITEM_FIELD_ITEM_TEXT_ID = OBJECT_END + 0x0033, // Size: 1, Type: INT, Flags: OWNER_ONLY + ITEM_FIELD_DURABILITY = OBJECT_END + 0x0034, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2 + ITEM_FIELD_MAXDURABILITY = OBJECT_END + 0x0035, // Size: 1, Type: INT, Flags: OWNER_ONLY, UNK2 + ITEM_END = OBJECT_END + 0x0036, +}; + +enum EContainerFields +{ + CONTAINER_FIELD_NUM_SLOTS = ITEM_END + 0x0000, // Size: 1, Type: INT, Flags: PUBLIC + CONTAINER_ALIGN_PAD = ITEM_END + 0x0001, // Size: 1, Type: BYTES, Flags: NONE + CONTAINER_FIELD_SLOT_1 = ITEM_END + 0x0002, // Size: 72, Type: LONG, Flags: PUBLIC + CONTAINER_END = ITEM_END + 0x004A, +}; + +enum EUnitFields +{ + UNIT_FIELD_CHARM = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_SUMMON = OBJECT_END + 0x0002, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_CHARMEDBY = OBJECT_END + 0x0004, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_SUMMONEDBY = OBJECT_END + 0x0006, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_CREATEDBY = OBJECT_END + 0x0008, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_TARGET = OBJECT_END + 0x000A, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_PERSUADED = OBJECT_END + 0x000C, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_CHANNEL_OBJECT = OBJECT_END + 0x000E, // Size: 2, Type: LONG, Flags: PUBLIC + UNIT_FIELD_HEALTH = OBJECT_END + 0x0010, // Size: 1, Type: INT, Flags: DYNAMIC + UNIT_FIELD_POWER1 = OBJECT_END + 0x0011, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER2 = OBJECT_END + 0x0012, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER3 = OBJECT_END + 0x0013, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER4 = OBJECT_END + 0x0014, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_POWER5 = OBJECT_END + 0x0015, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXHEALTH = OBJECT_END + 0x0016, // Size: 1, Type: INT, Flags: DYNAMIC + UNIT_FIELD_MAXPOWER1 = OBJECT_END + 0x0017, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER2 = OBJECT_END + 0x0018, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER3 = OBJECT_END + 0x0019, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER4 = OBJECT_END + 0x001A, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MAXPOWER5 = OBJECT_END + 0x001B, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_LEVEL = OBJECT_END + 0x001C, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_FACTIONTEMPLATE = OBJECT_END + 0x001D, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_BYTES_0 = OBJECT_END + 0x001E, // Size: 1, Type: BYTES, Flags: PUBLIC + UNIT_VIRTUAL_ITEM_SLOT_DISPLAY = OBJECT_END + 0x001F, // Size: 3, Type: INT, Flags: PUBLIC + UNIT_VIRTUAL_ITEM_INFO = OBJECT_END + 0x0022, // Size: 6, Type: BYTES, Flags: PUBLIC + UNIT_FIELD_FLAGS = OBJECT_END + 0x0028, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_FLAGS_2 = OBJECT_END + 0x0029, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_AURA = OBJECT_END + 0x002A, // Size: 56, Type: INT, Flags: PUBLIC + UNIT_FIELD_AURAFLAGS = OBJECT_END + 0x0062, // Size: 14, Type: BYTES, Flags: PUBLIC + UNIT_FIELD_AURALEVELS = OBJECT_END + 0x0070, // Size: 14, Type: BYTES, Flags: PUBLIC + UNIT_FIELD_AURAAPPLICATIONS = OBJECT_END + 0x007E, // Size: 14, Type: BYTES, Flags: PUBLIC + UNIT_FIELD_AURASTATE = OBJECT_END + 0x008C, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_BASEATTACKTIME = OBJECT_END + 0x008D, // Size: 2, Type: INT, Flags: PUBLIC + UNIT_FIELD_RANGEDATTACKTIME = OBJECT_END + 0x008F, // Size: 1, Type: INT, Flags: PRIVATE + UNIT_FIELD_BOUNDINGRADIUS = OBJECT_END + 0x0090, // Size: 1, Type: FLOAT, Flags: PUBLIC + UNIT_FIELD_COMBATREACH = OBJECT_END + 0x0091, // Size: 1, Type: FLOAT, Flags: PUBLIC + UNIT_FIELD_DISPLAYID = OBJECT_END + 0x0092, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_NATIVEDISPLAYID = OBJECT_END + 0x0093, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MOUNTDISPLAYID = OBJECT_END + 0x0094, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_MINDAMAGE = OBJECT_END + 0x0095, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 + UNIT_FIELD_MAXDAMAGE = OBJECT_END + 0x0096, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 + UNIT_FIELD_MINOFFHANDDAMAGE = OBJECT_END + 0x0097, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 + UNIT_FIELD_MAXOFFHANDDAMAGE = OBJECT_END + 0x0098, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY, UNK3 + UNIT_FIELD_BYTES_1 = OBJECT_END + 0x0099, // Size: 1, Type: BYTES, Flags: PUBLIC + UNIT_FIELD_PETNUMBER = OBJECT_END + 0x009A, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_PET_NAME_TIMESTAMP = OBJECT_END + 0x009B, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_PETEXPERIENCE = OBJECT_END + 0x009C, // Size: 1, Type: INT, Flags: OWNER_ONLY + UNIT_FIELD_PETNEXTLEVELEXP = OBJECT_END + 0x009D, // Size: 1, Type: INT, Flags: OWNER_ONLY + UNIT_DYNAMIC_FLAGS = OBJECT_END + 0x009E, // Size: 1, Type: INT, Flags: DYNAMIC + UNIT_CHANNEL_SPELL = OBJECT_END + 0x009F, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_MOD_CAST_SPEED = OBJECT_END + 0x00A0, // Size: 1, Type: FLOAT, Flags: PUBLIC + UNIT_CREATED_BY_SPELL = OBJECT_END + 0x00A1, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_NPC_FLAGS = OBJECT_END + 0x00A2, // Size: 1, Type: INT, Flags: DYNAMIC + UNIT_NPC_EMOTESTATE = OBJECT_END + 0x00A3, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_TRAINING_POINTS = OBJECT_END + 0x00A4, // Size: 1, Type: TWO_SHORT, Flags: OWNER_ONLY + UNIT_FIELD_STAT0 = OBJECT_END + 0x00A5, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_STAT1 = OBJECT_END + 0x00A6, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_STAT2 = OBJECT_END + 0x00A7, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_STAT3 = OBJECT_END + 0x00A8, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_STAT4 = OBJECT_END + 0x00A9, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POSSTAT0 = OBJECT_END + 0x00AA, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POSSTAT1 = OBJECT_END + 0x00AB, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POSSTAT2 = OBJECT_END + 0x00AC, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POSSTAT3 = OBJECT_END + 0x00AD, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POSSTAT4 = OBJECT_END + 0x00AE, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_NEGSTAT0 = OBJECT_END + 0x00AF, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_NEGSTAT1 = OBJECT_END + 0x00B0, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_NEGSTAT2 = OBJECT_END + 0x00B1, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_NEGSTAT3 = OBJECT_END + 0x00B2, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_NEGSTAT4 = OBJECT_END + 0x00B3, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_RESISTANCES = OBJECT_END + 0x00B4, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY, UNK3 + UNIT_FIELD_RESISTANCEBUFFMODSPOSITIVE = OBJECT_END + 0x00BB, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_RESISTANCEBUFFMODSNEGATIVE = OBJECT_END + 0x00C2, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_BASE_MANA = OBJECT_END + 0x00C9, // Size: 1, Type: INT, Flags: PUBLIC + UNIT_FIELD_BASE_HEALTH = OBJECT_END + 0x00CA, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_BYTES_2 = OBJECT_END + 0x00CB, // Size: 1, Type: BYTES, Flags: PUBLIC + UNIT_FIELD_ATTACK_POWER = OBJECT_END + 0x00CC, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_ATTACK_POWER_MODS = OBJECT_END + 0x00CD, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_ATTACK_POWER_MULTIPLIER = OBJECT_END + 0x00CE, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_RANGED_ATTACK_POWER = OBJECT_END + 0x00CF, // Size: 1, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_RANGED_ATTACK_POWER_MODS = OBJECT_END + 0x00D0, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER = OBJECT_END + 0x00D1, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_MINRANGEDDAMAGE = OBJECT_END + 0x00D2, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_MAXRANGEDDAMAGE = OBJECT_END + 0x00D3, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POWER_COST_MODIFIER = OBJECT_END + 0x00D4, // Size: 7, Type: INT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_POWER_COST_MULTIPLIER = OBJECT_END + 0x00DB, // Size: 7, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_MAXHEALTHMODIFIER = OBJECT_END + 0x00E2, // Size: 1, Type: FLOAT, Flags: PRIVATE, OWNER_ONLY + UNIT_FIELD_PADDING = OBJECT_END + 0x00E3, // Size: 1, Type: INT, Flags: NONE + UNIT_END = OBJECT_END + 0x00E4, + + PLAYER_DUEL_ARBITER = UNIT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_FLAGS = UNIT_END + 0x0002, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_GUILDID = UNIT_END + 0x0003, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_GUILDRANK = UNIT_END + 0x0004, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_BYTES = UNIT_END + 0x0005, // Size: 1, Type: BYTES, Flags: PUBLIC + PLAYER_BYTES_2 = UNIT_END + 0x0006, // Size: 1, Type: BYTES, Flags: PUBLIC + PLAYER_BYTES_3 = UNIT_END + 0x0007, // Size: 1, Type: BYTES, Flags: PUBLIC + PLAYER_DUEL_TEAM = UNIT_END + 0x0008, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_GUILD_TIMESTAMP = UNIT_END + 0x0009, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_QUEST_LOG_1_1 = UNIT_END + 0x000A, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_1_2 = UNIT_END + 0x000B, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_1_3 = UNIT_END + 0x000C, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_1_4 = UNIT_END + 0x000D, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_2_1 = UNIT_END + 0x000E, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_2_2 = UNIT_END + 0x000F, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_2_3 = UNIT_END + 0x0010, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_2_4 = UNIT_END + 0x0011, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_3_1 = UNIT_END + 0x0012, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_3_2 = UNIT_END + 0x0013, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_3_3 = UNIT_END + 0x0014, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_3_4 = UNIT_END + 0x0015, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_4_1 = UNIT_END + 0x0016, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_4_2 = UNIT_END + 0x0017, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_4_3 = UNIT_END + 0x0018, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_4_4 = UNIT_END + 0x0019, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_5_1 = UNIT_END + 0x001A, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_5_2 = UNIT_END + 0x001B, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_5_3 = UNIT_END + 0x001C, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_5_4 = UNIT_END + 0x001D, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_6_1 = UNIT_END + 0x001E, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_6_2 = UNIT_END + 0x001F, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_6_3 = UNIT_END + 0x0020, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_6_4 = UNIT_END + 0x0021, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_7_1 = UNIT_END + 0x0022, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_7_2 = UNIT_END + 0x0023, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_7_3 = UNIT_END + 0x0024, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_7_4 = UNIT_END + 0x0025, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_8_1 = UNIT_END + 0x0026, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_8_2 = UNIT_END + 0x0027, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_8_3 = UNIT_END + 0x0028, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_8_4 = UNIT_END + 0x0029, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_9_1 = UNIT_END + 0x002A, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_9_2 = UNIT_END + 0x002B, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_9_3 = UNIT_END + 0x002C, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_9_4 = UNIT_END + 0x002D, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_10_1 = UNIT_END + 0x002E, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_10_2 = UNIT_END + 0x002F, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_10_3 = UNIT_END + 0x0030, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_10_4 = UNIT_END + 0x0031, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_11_1 = UNIT_END + 0x0032, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_11_2 = UNIT_END + 0x0033, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_11_3 = UNIT_END + 0x0034, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_11_4 = UNIT_END + 0x0035, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_12_1 = UNIT_END + 0x0036, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_12_2 = UNIT_END + 0x0037, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_12_3 = UNIT_END + 0x0038, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_12_4 = UNIT_END + 0x0039, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_13_1 = UNIT_END + 0x003A, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_13_2 = UNIT_END + 0x003B, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_13_3 = UNIT_END + 0x003C, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_13_4 = UNIT_END + 0x003D, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_14_1 = UNIT_END + 0x003E, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_14_2 = UNIT_END + 0x003F, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_14_3 = UNIT_END + 0x0040, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_14_4 = UNIT_END + 0x0041, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_15_1 = UNIT_END + 0x0042, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_15_2 = UNIT_END + 0x0043, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_15_3 = UNIT_END + 0x0044, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_15_4 = UNIT_END + 0x0045, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_16_1 = UNIT_END + 0x0046, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_16_2 = UNIT_END + 0x0047, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_16_3 = UNIT_END + 0x0048, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_16_4 = UNIT_END + 0x0049, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_17_1 = UNIT_END + 0x004A, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_17_2 = UNIT_END + 0x004B, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_17_3 = UNIT_END + 0x004C, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_17_4 = UNIT_END + 0x004D, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_18_1 = UNIT_END + 0x004E, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_18_2 = UNIT_END + 0x004F, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_18_3 = UNIT_END + 0x0050, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_18_4 = UNIT_END + 0x0051, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_19_1 = UNIT_END + 0x0052, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_19_2 = UNIT_END + 0x0053, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_19_3 = UNIT_END + 0x0054, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_19_4 = UNIT_END + 0x0055, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_20_1 = UNIT_END + 0x0056, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_20_2 = UNIT_END + 0x0057, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_20_3 = UNIT_END + 0x0058, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_20_4 = UNIT_END + 0x0059, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_21_1 = UNIT_END + 0x005A, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_21_2 = UNIT_END + 0x005B, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_21_3 = UNIT_END + 0x005C, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_21_4 = UNIT_END + 0x005D, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_22_1 = UNIT_END + 0x005E, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_22_2 = UNIT_END + 0x005F, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_22_3 = UNIT_END + 0x0060, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_22_4 = UNIT_END + 0x0061, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_23_1 = UNIT_END + 0x0062, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_23_2 = UNIT_END + 0x0063, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_23_3 = UNIT_END + 0x0064, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_23_4 = UNIT_END + 0x0065, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_24_1 = UNIT_END + 0x0066, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_24_2 = UNIT_END + 0x0067, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_24_3 = UNIT_END + 0x0068, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_24_4 = UNIT_END + 0x0069, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_25_1 = UNIT_END + 0x006A, // Size: 1, Type: INT, Flags: GROUP_ONLY + PLAYER_QUEST_LOG_25_2 = UNIT_END + 0x006B, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_QUEST_LOG_25_3 = UNIT_END + 0x006C, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_QUEST_LOG_25_4 = UNIT_END + 0x006D, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_VISIBLE_ITEM_1_CREATOR = UNIT_END + 0x006E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_1_0 = UNIT_END + 0x0070, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_1_PROPERTIES = UNIT_END + 0x007C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_1_PAD = UNIT_END + 0x007D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_2_CREATOR = UNIT_END + 0x007E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_2_0 = UNIT_END + 0x0080, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_2_PROPERTIES = UNIT_END + 0x008C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_2_PAD = UNIT_END + 0x008D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_3_CREATOR = UNIT_END + 0x008E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_3_0 = UNIT_END + 0x0090, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_3_PROPERTIES = UNIT_END + 0x009C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_3_PAD = UNIT_END + 0x009D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_4_CREATOR = UNIT_END + 0x009E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_4_0 = UNIT_END + 0x00A0, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_4_PROPERTIES = UNIT_END + 0x00AC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_4_PAD = UNIT_END + 0x00AD, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_5_CREATOR = UNIT_END + 0x00AE, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_5_0 = UNIT_END + 0x00B0, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_5_PROPERTIES = UNIT_END + 0x00BC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_5_PAD = UNIT_END + 0x00BD, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_6_CREATOR = UNIT_END + 0x00BE, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_6_0 = UNIT_END + 0x00C0, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_6_PROPERTIES = UNIT_END + 0x00CC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_6_PAD = UNIT_END + 0x00CD, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_7_CREATOR = UNIT_END + 0x00CE, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_7_0 = UNIT_END + 0x00D0, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_7_PROPERTIES = UNIT_END + 0x00DC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_7_PAD = UNIT_END + 0x00DD, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_8_CREATOR = UNIT_END + 0x00DE, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_8_0 = UNIT_END + 0x00E0, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_8_PROPERTIES = UNIT_END + 0x00EC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_8_PAD = UNIT_END + 0x00ED, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_9_CREATOR = UNIT_END + 0x00EE, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_9_0 = UNIT_END + 0x00F0, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_9_PROPERTIES = UNIT_END + 0x00FC, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_9_PAD = UNIT_END + 0x00FD, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_10_CREATOR = UNIT_END + 0x00FE, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_10_0 = UNIT_END + 0x0100, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_10_PROPERTIES = UNIT_END + 0x010C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_10_PAD = UNIT_END + 0x010D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_11_CREATOR = UNIT_END + 0x010E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_11_0 = UNIT_END + 0x0110, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_11_PROPERTIES = UNIT_END + 0x011C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_11_PAD = UNIT_END + 0x011D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_12_CREATOR = UNIT_END + 0x011E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_12_0 = UNIT_END + 0x0120, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_12_PROPERTIES = UNIT_END + 0x012C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_12_PAD = UNIT_END + 0x012D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_13_CREATOR = UNIT_END + 0x012E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_13_0 = UNIT_END + 0x0130, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_13_PROPERTIES = UNIT_END + 0x013C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_13_PAD = UNIT_END + 0x013D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_14_CREATOR = UNIT_END + 0x013E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_14_0 = UNIT_END + 0x0140, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_14_PROPERTIES = UNIT_END + 0x014C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_14_PAD = UNIT_END + 0x014D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_15_CREATOR = UNIT_END + 0x014E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_15_0 = UNIT_END + 0x0150, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_15_PROPERTIES = UNIT_END + 0x015C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_15_PAD = UNIT_END + 0x015D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_16_CREATOR = UNIT_END + 0x015E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_16_0 = UNIT_END + 0x0160, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_16_PROPERTIES = UNIT_END + 0x016C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_16_PAD = UNIT_END + 0x016D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_17_CREATOR = UNIT_END + 0x016E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_17_0 = UNIT_END + 0x0170, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_17_PROPERTIES = UNIT_END + 0x017C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_17_PAD = UNIT_END + 0x017D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_18_CREATOR = UNIT_END + 0x017E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_18_0 = UNIT_END + 0x0180, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_18_PROPERTIES = UNIT_END + 0x018C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_18_PAD = UNIT_END + 0x018D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_19_CREATOR = UNIT_END + 0x018E, // Size: 2, Type: LONG, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_19_0 = UNIT_END + 0x0190, // Size: 12, Type: INT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_19_PROPERTIES = UNIT_END + 0x019C, // Size: 1, Type: TWO_SHORT, Flags: PUBLIC + PLAYER_VISIBLE_ITEM_19_PAD = UNIT_END + 0x019D, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_CHOSEN_TITLE = UNIT_END + 0x019E, // Size: 1, Type: INT, Flags: PUBLIC + PLAYER_FIELD_PAD_0 = UNIT_END + 0x019F, // Size: 1, Type: INT, Flags: NONE + PLAYER_FIELD_INV_SLOT_HEAD = UNIT_END + 0x01A0, // Size: 46, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_PACK_SLOT_1 = UNIT_END + 0x01CE, // Size: 32, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_BANK_SLOT_1 = UNIT_END + 0x01EE, // Size: 56, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_BANKBAG_SLOT_1 = UNIT_END + 0x0226, // Size: 14, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_VENDORBUYBACK_SLOT_1 = UNIT_END + 0x0234, // Size: 24, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_KEYRING_SLOT_1 = UNIT_END + 0x024C, // Size: 64, Type: LONG, Flags: PRIVATE + PLAYER_FIELD_VANITYPET_SLOT_1 = UNIT_END + 0x028C, // Size: 36, Type: LONG, Flags: PRIVATE + PLAYER_FARSIGHT = UNIT_END + 0x02B0, // Size: 2, Type: LONG, Flags: PRIVATE + PLAYER__FIELD_KNOWN_TITLES = UNIT_END + 0x02B2, // Size: 2, Type: LONG, Flags: PRIVATE + PLAYER_XP = UNIT_END + 0x02B4, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_NEXT_LEVEL_XP = UNIT_END + 0x02B5, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_SKILL_INFO_1_1 = UNIT_END + 0x02B6, // Size: 384, Type: TWO_SHORT, Flags: PRIVATE + PLAYER_CHARACTER_POINTS1 = UNIT_END + 0x0436, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_CHARACTER_POINTS2 = UNIT_END + 0x0437, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_TRACK_CREATURES = UNIT_END + 0x0438, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_TRACK_RESOURCES = UNIT_END + 0x0439, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_BLOCK_PERCENTAGE = UNIT_END + 0x043A, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_DODGE_PERCENTAGE = UNIT_END + 0x043B, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_PARRY_PERCENTAGE = UNIT_END + 0x043C, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_EXPERTISE = UNIT_END + 0x043D, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_OFFHAND_EXPERTISE = UNIT_END + 0x043E, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_CRIT_PERCENTAGE = UNIT_END + 0x043F, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_RANGED_CRIT_PERCENTAGE = UNIT_END + 0x0440, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_OFFHAND_CRIT_PERCENTAGE = UNIT_END + 0x0441, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_SPELL_CRIT_PERCENTAGE1 = UNIT_END + 0x0442, // Size: 7, Type: FLOAT, Flags: PRIVATE + PLAYER_SHIELD_BLOCK = UNIT_END + 0x0449, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_EXPLORED_ZONES_1 = UNIT_END + 0x044A, // Size: 128, Type: BYTES, Flags: PRIVATE + PLAYER_REST_STATE_EXPERIENCE = UNIT_END + 0x04CA, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_COINAGE = UNIT_END + 0x04CB, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_DAMAGE_DONE_POS = UNIT_END + 0x04CC, // Size: 7, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_DAMAGE_DONE_NEG = UNIT_END + 0x04D3, // Size: 7, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_DAMAGE_DONE_PCT = UNIT_END + 0x04DA, // Size: 7, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_HEALING_DONE_POS = UNIT_END + 0x04E1, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_TARGET_RESISTANCE = UNIT_END + 0x04E2, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE = UNIT_END + 0x04E3, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_BYTES = UNIT_END + 0x04E4, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_AMMO_ID = UNIT_END + 0x04E5, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_SELF_RES_SPELL = UNIT_END + 0x04E6, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_PVP_MEDALS = UNIT_END + 0x04E7, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_BUYBACK_PRICE_1 = UNIT_END + 0x04E8, // Size: 12, Type: INT, Flags: PRIVATE + PLAYER_FIELD_BUYBACK_TIMESTAMP_1 = UNIT_END + 0x04F4, // Size: 12, Type: INT, Flags: PRIVATE + PLAYER_FIELD_KILLS = UNIT_END + 0x0500, // Size: 1, Type: TWO_SHORT, Flags: PRIVATE + PLAYER_FIELD_TODAY_CONTRIBUTION = UNIT_END + 0x0501, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_YESTERDAY_CONTRIBUTION = UNIT_END + 0x0502, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_LIFETIME_HONORBALE_KILLS = UNIT_END + 0x0503, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_BYTES2 = UNIT_END + 0x0504, // Size: 1, Type: BYTES, Flags: PRIVATE + PLAYER_FIELD_WATCHED_FACTION_INDEX = UNIT_END + 0x0505, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_COMBAT_RATING_1 = UNIT_END + 0x0506, // Size: 24, Type: INT, Flags: PRIVATE + PLAYER_FIELD_ARENA_TEAM_INFO_1_1 = UNIT_END + 0x051E, // Size: 18, Type: INT, Flags: PRIVATE + PLAYER_FIELD_HONOR_CURRENCY = UNIT_END + 0x0530, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_ARENA_CURRENCY = UNIT_END + 0x0531, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_MOD_MANA_REGEN = UNIT_END + 0x0532, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_FIELD_MOD_MANA_REGEN_INTERRUPT = UNIT_END + 0x0533, // Size: 1, Type: FLOAT, Flags: PRIVATE + PLAYER_FIELD_MAX_LEVEL = UNIT_END + 0x0534, // Size: 1, Type: INT, Flags: PRIVATE + PLAYER_FIELD_DAILY_QUESTS_1 = UNIT_END + 0x0535, // Size: 25, Type: INT, Flags: PRIVATE + PLAYER_END = UNIT_END + 0x054E, +}; + +enum EGameObjectFields +{ + OBJECT_FIELD_CREATED_BY = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC + GAMEOBJECT_DISPLAYID = OBJECT_END + 0x0002, // Size: 1, Type: INT, Flags: PUBLIC + GAMEOBJECT_FLAGS = OBJECT_END + 0x0003, // Size: 1, Type: INT, Flags: PUBLIC + GAMEOBJECT_ROTATION = OBJECT_END + 0x0004, // Size: 4, Type: FLOAT, Flags: PUBLIC + GAMEOBJECT_STATE = OBJECT_END + 0x0008, // Size: 1, Type: INT, Flags: PUBLIC + GAMEOBJECT_POS_X = OBJECT_END + 0x0009, // Size: 1, Type: FLOAT, Flags: PUBLIC + GAMEOBJECT_POS_Y = OBJECT_END + 0x000A, // Size: 1, Type: FLOAT, Flags: PUBLIC + GAMEOBJECT_POS_Z = OBJECT_END + 0x000B, // Size: 1, Type: FLOAT, Flags: PUBLIC + GAMEOBJECT_FACING = OBJECT_END + 0x000C, // Size: 1, Type: FLOAT, Flags: PUBLIC + GAMEOBJECT_DYN_FLAGS = OBJECT_END + 0x000D, // Size: 1, Type: INT, Flags: DYNAMIC + GAMEOBJECT_FACTION = OBJECT_END + 0x000E, // Size: 1, Type: INT, Flags: PUBLIC + GAMEOBJECT_TYPE_ID = OBJECT_END + 0x000F, // Size: 1, Type: INT, Flags: PUBLIC + GAMEOBJECT_LEVEL = OBJECT_END + 0x0010, // Size: 1, Type: INT, Flags: PUBLIC + GAMEOBJECT_ARTKIT = OBJECT_END + 0x0011, // Size: 1, Type: INT, Flags: PUBLIC + GAMEOBJECT_ANIMPROGRESS = OBJECT_END + 0x0012, // Size: 1, Type: INT, Flags: DYNAMIC + GAMEOBJECT_PADDING = OBJECT_END + 0x0013, // Size: 1, Type: INT, Flags: NONE + GAMEOBJECT_END = OBJECT_END + 0x0014, +}; + +enum EDynamicObjectFields +{ + DYNAMICOBJECT_CASTER = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC + DYNAMICOBJECT_BYTES = OBJECT_END + 0x0002, // Size: 1, Type: BYTES, Flags: PUBLIC + DYNAMICOBJECT_SPELLID = OBJECT_END + 0x0003, // Size: 1, Type: INT, Flags: PUBLIC + DYNAMICOBJECT_RADIUS = OBJECT_END + 0x0004, // Size: 1, Type: FLOAT, Flags: PUBLIC + DYNAMICOBJECT_POS_X = OBJECT_END + 0x0005, // Size: 1, Type: FLOAT, Flags: PUBLIC + DYNAMICOBJECT_POS_Y = OBJECT_END + 0x0006, // Size: 1, Type: FLOAT, Flags: PUBLIC + DYNAMICOBJECT_POS_Z = OBJECT_END + 0x0007, // Size: 1, Type: FLOAT, Flags: PUBLIC + DYNAMICOBJECT_FACING = OBJECT_END + 0x0008, // Size: 1, Type: FLOAT, Flags: PUBLIC + DYNAMICOBJECT_CASTTIME = OBJECT_END + 0x0009, // Size: 1, Type: INT, Flags: PUBLIC + DYNAMICOBJECT_END = OBJECT_END + 0x000A, +}; + +enum ECorpseFields +{ + CORPSE_FIELD_OWNER = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC + CORPSE_FIELD_PARTY = OBJECT_END + 0x0002, // Size: 2, Type: LONG, Flags: PUBLIC + CORPSE_FIELD_FACING = OBJECT_END + 0x0004, // Size: 1, Type: FLOAT, Flags: PUBLIC + CORPSE_FIELD_POS_X = OBJECT_END + 0x0005, // Size: 1, Type: FLOAT, Flags: PUBLIC + CORPSE_FIELD_POS_Y = OBJECT_END + 0x0006, // Size: 1, Type: FLOAT, Flags: PUBLIC + CORPSE_FIELD_POS_Z = OBJECT_END + 0x0007, // Size: 1, Type: FLOAT, Flags: PUBLIC + CORPSE_FIELD_DISPLAY_ID = OBJECT_END + 0x0008, // Size: 1, Type: INT, Flags: PUBLIC + CORPSE_FIELD_ITEM = OBJECT_END + 0x0009, // Size: 19, Type: INT, Flags: PUBLIC + CORPSE_FIELD_BYTES_1 = OBJECT_END + 0x001C, // Size: 1, Type: BYTES, Flags: PUBLIC + CORPSE_FIELD_BYTES_2 = OBJECT_END + 0x001D, // Size: 1, Type: BYTES, Flags: PUBLIC + CORPSE_FIELD_GUILD = OBJECT_END + 0x001E, // Size: 1, Type: INT, Flags: PUBLIC + CORPSE_FIELD_FLAGS = OBJECT_END + 0x001F, // Size: 1, Type: INT, Flags: PUBLIC + CORPSE_FIELD_DYNAMIC_FLAGS = OBJECT_END + 0x0020, // Size: 1, Type: INT, Flags: DYNAMIC + CORPSE_FIELD_PAD = OBJECT_END + 0x0021, // Size: 1, Type: INT, Flags: NONE + CORPSE_END = OBJECT_END + 0x0022, +}; +#endif diff --git a/src/game/UpdateMask.h b/src/game/UpdateMask.h new file mode 100644 index 00000000000..7b0b7bb0c6d --- /dev/null +++ b/src/game/UpdateMask.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __UPDATEMASK_H +#define __UPDATEMASK_H + +#include "UpdateFields.h" +#include "Errors.h" + +class UpdateMask +{ + public: + UpdateMask( ) : mCount( 0 ), mBlocks( 0 ), mUpdateMask( 0 ) { } + UpdateMask( const UpdateMask& mask ) : mUpdateMask( 0 ) { *this = mask; } + + ~UpdateMask( ) + { + if(mUpdateMask) + delete [] mUpdateMask; + } + + inline void SetBit (uint32 index) + { + ( (uint8 *)mUpdateMask )[ index >> 3 ] |= 1 << ( index & 0x7 ); + } + + inline void UnsetBit (uint32 index) + { + ( (uint8 *)mUpdateMask )[ index >> 3 ] &= (0xff ^ (1 << ( index & 0x7 ) ) ); + } + + inline bool GetBit (uint32 index) + { + return ( ( (uint8 *)mUpdateMask)[ index >> 3 ] & ( 1 << ( index & 0x7 ) )) != 0; + } + + inline uint32 GetBlockCount() { return mBlocks; } + inline uint32 GetLength() { return mBlocks << 2; } + inline uint32 GetCount() { return mCount; } + inline uint8* GetMask() { return (uint8*)mUpdateMask; } + + inline void SetCount (uint32 valuesCount) + { + if(mUpdateMask) + delete [] mUpdateMask; + + mCount = valuesCount; + mBlocks = (valuesCount + 31) / 32; + + mUpdateMask = new uint32[mBlocks]; + memset(mUpdateMask, 0, mBlocks << 2); + } + + inline void Clear() + { + if (mUpdateMask) + memset(mUpdateMask, 0, mBlocks << 2); + } + + inline UpdateMask& operator = ( const UpdateMask& mask ) + { + SetCount(mask.mCount); + memcpy(mUpdateMask, mask.mUpdateMask, mBlocks << 2); + + return *this; + } + + inline void operator &= ( const UpdateMask& mask ) + { + ASSERT(mask.mCount <= mCount); + for (uint32 i = 0; i < mBlocks; i++) + mUpdateMask[i] &= mask.mUpdateMask[i]; + } + + inline void operator |= ( const UpdateMask& mask ) + { + ASSERT(mask.mCount <= mCount); + for (uint32 i = 0; i < mBlocks; i++) + mUpdateMask[i] |= mask.mUpdateMask[i]; + } + + inline UpdateMask operator & ( const UpdateMask& mask ) const + { + ASSERT(mask.mCount <= mCount); + + UpdateMask newmask; + newmask = *this; + newmask &= mask; + + return newmask; + } + + inline UpdateMask operator | ( const UpdateMask& mask ) const + { + ASSERT(mask.mCount <= mCount); + + UpdateMask newmask; + newmask = *this; + newmask |= mask; + + return newmask; + } + + private: + uint32 mCount; + uint32 mBlocks; + uint32 *mUpdateMask; +}; +#endif diff --git a/src/game/VoiceChatHandler.cpp b/src/game/VoiceChatHandler.cpp new file mode 100644 index 00000000000..1368ddc4f85 --- /dev/null +++ b/src/game/VoiceChatHandler.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "Opcodes.h" +#include "Log.h" + +void WorldSession::HandleVoiceSettingsOpcode( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: CMSG_VOICE_SETTINGS"); + // uint8 isVoiceEnabled, uint8 isMicrophoneEnabled + recv_data.hexlike(); +} + +void WorldSession::HandleChannelEnableVoiceOpcode( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: CMSG_CHANNEL_ENABLE_VOICE"); + // Enable Voice button in channel context menu + recv_data.hexlike(); +} + +void WorldSession::HandleChannelVoiceChatQuery( WorldPacket & recv_data ) +{ + sLog.outDebug("WORLD: CMSG_CHANNEL_VOICE_CHAT_QUERY"); + // uint32, string + recv_data.hexlike(); +} diff --git a/src/game/WaypointManager.cpp b/src/game/WaypointManager.cpp new file mode 100644 index 00000000000..a4b809ddbef --- /dev/null +++ b/src/game/WaypointManager.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Database/DatabaseEnv.h" +#include "GridDefines.h" +#include "Policies/SingletonImp.h" +#include "WaypointManager.h" +#include "ProgressBar.h" +#include "MapManager.h" + +INSTANTIATE_SINGLETON_1(WaypointManager); + +bool WaypointBehavior::isEmpty() +{ + return emote == 0 && spell == 0 && model1 == 0 && model2 == 0 && text[0].empty() && + text[1].empty() && text[2].empty() && text[3].empty() && text[4].empty(); +} + +WaypointBehavior::WaypointBehavior(const WaypointBehavior &b) +{ + emote = b.emote; spell = b.spell; model1 = b.model1; model2 = b.model2; + text[0] = b.text[0]; text[1] = b.text[1]; text[2] = b.text[2]; + text[3] = b.text[3]; text[4] = b.text[4]; +} + +void WaypointManager::Load() +{ + Cleanup(); + + uint32 total_paths = 0; + uint32 total_nodes = 0; + uint32 total_behaviors = 0; + + QueryResult *result = WorldDatabase.Query("SELECT id, COUNT(point) FROM creature_movement GROUP BY id"); + if(result) + { + total_paths = result->GetRowCount(); + barGoLink bar( total_paths ); + do + { + Field *fields = result->Fetch(); + uint32 id = fields[0].GetUInt32(); + uint32 count = fields[1].GetUInt32(); + m_pathMap[id].resize(count); + + total_nodes += count; + bar.step(); + } while( result->NextRow() ); + delete result; + } + + result = WorldDatabase.Query("SELECT position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5, id, point FROM creature_movement"); + if(result) + { + barGoLink bar( result->GetRowCount() ); + do + { + Field *fields = result->Fetch(); + uint32 point = fields[15].GetUInt32(); + uint32 id = fields[14].GetUInt32(); + + WaypointPath &path = m_pathMap[id]; + // the cleanup queries make sure the following is true + assert(point >= 1 && point <= path.size()); + WaypointNode &node = path[point-1]; + + node.x = fields[0].GetFloat(); + node.y = fields[1].GetFloat(); + node.z = fields[2].GetFloat(); + node.orientation = fields[3].GetFloat(); + node.delay = fields[6].GetUInt16(); + + // prevent using invalid coordinates + if(!MaNGOS::IsValidMapCoord(node.x, node.y, node.z, node.orientation)) + { + QueryResult *result1 = WorldDatabase.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id); + if(result1) sLog.outErrorDb("ERROR: Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %d, Y: %d).", id, result1->Fetch()[0].GetUInt32(), point, node.x, node.y); + else sLog.outErrorDb("ERROR: Waypoint path %d, have invalid coordinates in his waypoint %d (X: %d, Y: %d).", id, point, node.x, node.y); + + MaNGOS::NormalizeMapCoord(node.x); + MaNGOS::NormalizeMapCoord(node.y); + if(result1) + { + node.z = MapManager::Instance ().GetBaseMap(result1->Fetch()[1].GetUInt32())->GetHeight(node.x, node.y, node.z); + delete result1; + } + WorldDatabase.PExecute("UPDATE creature_movement SET position_x = '%f', position_y = '%f', position_z = '%f' WHERE id = '%u' AND point = '%u'", node.x, node.y, node.z, id, point); + } + + WaypointBehavior be; + be.model1 = fields[4].GetUInt32(); + be.model2 = fields[5].GetUInt32(); + be.emote = fields[7].GetUInt32(); + be.spell = fields[8].GetUInt32(); + be.text[0] = fields[9].GetCppString(); + be.text[1] = fields[10].GetCppString(); + be.text[2] = fields[11].GetCppString(); + be.text[3] = fields[12].GetCppString(); + be.text[4] = fields[13].GetCppString(); + + // save memory by not storing empty behaviors + if(!be.isEmpty()) + { + node.behavior = new WaypointBehavior(be); + ++total_behaviors; + } + else + node.behavior = NULL; + bar.step(); + } while( result->NextRow() ); + delete result; + } + sLog.outString( ">> Loaded %u paths, %u nodes and %u behaviors", total_paths, total_nodes, total_behaviors); +} + +void WaypointManager::Cleanup() +{ + // check if points need to be renumbered and do it + if(QueryResult *result = WorldDatabase.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1")) + { + delete result; + WorldDatabase.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement"); + WorldDatabase.DirectExecute("INSERT INTO temp SELECT * FROM creature_movement"); + WorldDatabase.DirectExecute("ALTER TABLE creature_movement DROP PRIMARY KEY"); + WorldDatabase.DirectExecute("UPDATE creature_movement AS T SET point = (SELECT COUNT(*) FROM temp WHERE id = T.id AND point <= T.point)"); + WorldDatabase.DirectExecute("ALTER TABLE creature_movement ADD PRIMARY KEY (id, point)"); + WorldDatabase.DirectExecute("DROP TABLE temp"); + assert(!(result = WorldDatabase.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1"))); + } +} + +void WaypointManager::Unload() +{ + for(WaypointPathMap::iterator itr = m_pathMap.begin(); itr != m_pathMap.end(); ++itr) + _clearPath(itr->second); + m_pathMap.clear(); +} + +void WaypointManager::_clearPath(WaypointPath &path) +{ + for(WaypointPath::iterator itr = path.begin(); itr != path.end(); ++itr) + if(itr->behavior) + delete itr->behavior; + path.clear(); +} + +/// - Insert after the last point +void WaypointManager::AddLastNode(uint32 id, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) +{ + _addNode(id, GetLastPoint(id, 0) + 1, x, y, z, o, delay, wpGuid); +} + +/// - Insert after a certain point +void WaypointManager::AddAfterNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) +{ + for(uint32 i = GetLastPoint(id, 0); i > point; i--) + WorldDatabase.PExecuteLog("UPDATE creature_movement SET point=point+1 WHERE id='%u' AND point='%u'", id, i); + + _addNode(id, point + 1, x, y, z, o, delay, wpGuid); +} + +/// - Insert without checking for collision +void WaypointManager::_addNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) +{ + if(point == 0) return; // counted from 1 in the DB + WorldDatabase.PExecuteLog("INSERT INTO creature_movement (id,point,position_x,position_y,position_z,orientation,wpguid,waittime) VALUES ('%u','%u','%f', '%f', '%f', '%f', '%d', '%d')", id, point, x, y, z, o, wpGuid, delay); + WaypointPathMap::iterator itr = m_pathMap.find(id); + if(itr == m_pathMap.end()) + itr = m_pathMap.insert(WaypointPathMap::value_type(id, WaypointPath())).first; + itr->second.insert(itr->second.begin() + (point - 1), WaypointNode(x, y, z, o, delay, NULL)); +} + +uint32 WaypointManager::GetLastPoint(uint32 id, uint32 default_notfound) +{ + uint32 point = default_notfound; + /*QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'", id); + if( result ) + { + point = (*result)[0].GetUInt32()+1; + delete result; + }*/ + WaypointPathMap::iterator itr = m_pathMap.find(id); + if(itr != m_pathMap.end() && itr->second.size() != 0) + point = itr->second.size(); + return point; +} + +void WaypointManager::DeleteNode(uint32 id, uint32 point) +{ + if(point == 0) return; // counted from 1 in the DB + WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id='%u' AND point='%u'", id, point); + WorldDatabase.PExecuteLog("UPDATE creature_movement SET point=point-1 WHERE id='%u' AND point>'%u'", id, point); + WaypointPathMap::iterator itr = m_pathMap.find(id); + if(itr != m_pathMap.end() && point <= itr->second.size()) + itr->second.erase(itr->second.begin() + (point-1)); +} + +void WaypointManager::DeletePath(uint32 id) +{ + WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id='%u'", id); + WaypointPathMap::iterator itr = m_pathMap.find(id); + if(itr != m_pathMap.end()) + _clearPath(itr->second); + // the path is not removed from the map, just cleared + // WMGs have pointers to the path, so deleting them would crash + // this wastes some memory, but these functions are + // only meant to be called by GM commands +} + +void WaypointManager::SetNodePosition(uint32 id, uint32 point, float x, float y, float z) +{ + if(point == 0) return; // counted from 1 in the DB + WorldDatabase.PExecuteLog("UPDATE creature_movement SET position_x = '%f',position_y = '%f',position_z = '%f' where id = '%u' AND point='%u'", x, y, z, id, point); + WaypointPathMap::iterator itr = m_pathMap.find(id); + if(itr != m_pathMap.end() && point <= itr->second.size()) + { + itr->second[point-1].x = x; + itr->second[point-1].y = y; + itr->second[point-1].z = z; + } +} + +void WaypointManager::SetNodeText(uint32 id, uint32 point, const char *text_field, const char *text) +{ + if(point == 0) return; // counted from 1 in the DB + if(!text_field) return; + std::string field = text_field; + WorldDatabase.escape_string(field); + + if(!text) + { + WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s=NULL WHERE id='%u' AND point='%u'", field.c_str(), id, point); + } + else + { + std::string text2 = text; + WorldDatabase.escape_string(text2); + WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s='%s' WHERE id='%u' AND point='%u'", field.c_str(), text2.c_str(), id, point); + } + + WaypointPathMap::iterator itr = m_pathMap.find(id); + if(itr != m_pathMap.end() && point <= itr->second.size()) + { + WaypointNode &node = itr->second[point-1]; + if(!node.behavior) node.behavior = new WaypointBehavior(); + + if(field == "text1") node.behavior->text[0] = text ? text : ""; + if(field == "text2") node.behavior->text[1] = text ? text : ""; + if(field == "text3") node.behavior->text[2] = text ? text : ""; + if(field == "text4") node.behavior->text[3] = text ? text : ""; + if(field == "text5") node.behavior->text[4] = text ? text : ""; + if(field == "emote") node.behavior->emote = text ? atoi(text) : 0; + if(field == "spell") node.behavior->spell = text ? atoi(text) : 0; + if(field == "model1") node.behavior->model1 = text ? atoi(text) : 0; + if(field == "model2") node.behavior->model2 = text ? atoi(text) : 0; + } +} diff --git a/src/game/WaypointManager.h b/src/game/WaypointManager.h new file mode 100644 index 00000000000..f0b66b66077 --- /dev/null +++ b/src/game/WaypointManager.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_WAYPOINTMANAGER_H +#define MANGOS_WAYPOINTMANAGER_H + +#include +#include +#include "Utilities/HashMap.h" + +struct WaypointBehavior +{ + uint32 emote; + uint32 spell; + std::string text[5]; + uint32 model1; + uint32 model2; + + bool isEmpty(); + WaypointBehavior() {} + WaypointBehavior(const WaypointBehavior &b); +}; + +struct WaypointNode +{ + float x; + float y; + float z; + float orientation; + uint32 delay; + WaypointBehavior * behavior; + WaypointNode() {} + WaypointNode(float _x, float _y, float _z, float _o, uint32 _delay, WaypointBehavior * _behavior) + : x(_x), y(_y), z(_z), orientation(_o), delay(_delay), behavior(_behavior) {} +}; + +typedef std::vector WaypointPath; + +class WaypointManager +{ + public: + WaypointManager() {} + ~WaypointManager() { Unload(); } + + void Load(); + void Unload(); + + void Cleanup(); + + WaypointPath *GetPath(uint32 id) + { + WaypointPathMap::iterator itr = m_pathMap.find(id); + return itr != m_pathMap.end() ? &itr->second : NULL; + } + + void AddLastNode(uint32 id, float x, float y, float z, float o, uint32 delay, uint32 wpGuid); + void AddAfterNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid); + uint32 GetLastPoint(uint32 id, uint32 default_notfound); + void DeleteNode(uint32 id, uint32 point); + void DeletePath(uint32 id); + void SetNodePosition(uint32 id, uint32 point, float x, float y, float z); + void SetNodeText(uint32 id, uint32 point, const char *text_field, const char *text); + + private: + void _addNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid); + void _clearPath(WaypointPath &path); + + typedef HM_NAMESPACE::hash_map WaypointPathMap; + WaypointPathMap m_pathMap; +}; + +#define WaypointMgr MaNGOS::Singleton::Instance() + +#endif diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp new file mode 100644 index 00000000000..0389e2ebc4b --- /dev/null +++ b/src/game/WaypointMovementGenerator.cpp @@ -0,0 +1,648 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* +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 + +#include "WaypointMovementGenerator.h" +#include "ObjectMgr.h" +#include "Creature.h" +#include "DestinationHolderImp.h" +#include "CreatureAI.h" +#include "WaypointManager.h" + +#include + +//-----------------------------------------------// +void +WaypointMovementGenerator::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::ClearWaypoints() +{ + i_path = NULL; +} + +void +WaypointMovementGenerator::Initialize() +{ +} + +bool +WaypointMovementGenerator::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_STUNDED | 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); + 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); + 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::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 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 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/WaypointMovementGenerator.h b/src/game/WaypointMovementGenerator.h new file mode 100644 index 00000000000..94762a961b6 --- /dev/null +++ b/src/game/WaypointMovementGenerator.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOS_WAYPOINTMOVEMENTGENERATOR_H +#define MANGOS_WAYPOINTMOVEMENTGENERATOR_H + +/** @page PathMovementGenerator is used to generate movements + * of waypoints and flight paths. Each serves the purpose + * of generate activities so that it generates updated + * packets for the players. + */ + +#include "MovementGenerator.h" +#include "DestinationHolder.h" +#include "WaypointManager.h" +#include "Path.h" +#include "Traveller.h" + +#include "Player.h" + +#include +#include + +#define FLIGHT_TRAVEL_UPDATE 100 +#define STOP_TIME_FOR_PLAYER 3 * 60 * 1000 // 3 Minutes + +template +class MANGOS_DLL_SPEC PathMovementBase +{ + public: + PathMovementBase() : i_currentNode(0) {} + virtual ~PathMovementBase() {}; + + inline bool MovementInProgress(void) const { return i_currentNode < i_path.Size(); } + + // template pattern, not defined .. override required + void LoadPath(T &); + void ReloadPath(T &); + uint32 GetCurrentNode() const { return i_currentNode; } + + bool GetDestination(float& x, float& y, float& z) const { i_destinationHolder.GetDestination(x,y,z); return true; } + protected: + uint32 i_currentNode; + DestinationHolder< Traveller > i_destinationHolder; + P i_path; +}; + +/** WaypointMovementGenerator loads a series of way points + * from the DB and apply it to the creature's movement generator. + * Hence, the creature will move according to its predefined way points. + */ + +template +class MANGOS_DLL_SPEC WaypointMovementGenerator; + +template<> +class MANGOS_DLL_SPEC WaypointMovementGenerator +: public MovementGeneratorMedium< Creature, WaypointMovementGenerator >, +public PathMovementBase +{ + TimeTrackerSmall i_nextMoveTime; + std::vector i_hasDone; + public: + WaypointMovementGenerator(Creature &) : i_nextMoveTime(0) {} + ~WaypointMovementGenerator() { ClearWaypoints(); } + void Initialize(Creature &u) + { + i_nextMoveTime.Reset(0); // TODO: check the lower bound (0 is probably too small) + u.StopMoving(); + LoadPath(u); + } + void Finalize(Creature &) {} + void Reset(Creature &u) { ReloadPath(u); } + bool Update(Creature &u, const uint32 &diff); + + void MovementInform(Creature &); + + MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; } + + // now path movement implmementation + void LoadPath(Creature &c); + void ReloadPath(Creature &c) { ClearWaypoints(); LoadPath(c); } + + // Player stoping creature + bool IsStopedByPlayer() { return b_StopedByPlayer; } + void SetStopedByPlayer(bool val) { b_StopedByPlayer = val; } + + // statics + static void Initialize(void); + private: + void ClearWaypoints(); + bool b_StopedByPlayer; +}; + +/** FlightPathMovementGenerator generates movement of the player for the paths + * and hence generates ground and activities for the player. + */ +class MANGOS_DLL_SPEC FlightPathMovementGenerator +: public MovementGeneratorMedium< Player, FlightPathMovementGenerator >, +public PathMovementBase +{ + uint32 i_pathId; + std::vector i_mapIds; + public: + explicit FlightPathMovementGenerator(uint32 id, uint32 startNode = 0) : i_pathId(id) { i_currentNode = startNode; } + void Initialize(Player &); + void Finalize(Player &); + void Reset(Player &) {} + bool Update(Player &, const uint32 &); + MovementGeneratorType GetMovementGeneratorType() { return FLIGHT_MOTION_TYPE; } + + void LoadPath(Player &); + void ReloadPath(Player &) { /* don't reload flight path */ } + + Path& GetPath() { return i_path; } + uint32 GetPathAtMapEnd() const; + inline bool HasArrived() const { return (i_currentNode >= i_path.Size()); } + void SetCurrentNodeAfterTeleport(); + void SkipCurrentNode() { ++i_currentNode; } +}; +#endif diff --git a/src/game/Weather.cpp b/src/game/Weather.cpp new file mode 100644 index 00000000000..77e81f0b16d --- /dev/null +++ b/src/game/Weather.cpp @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup world +*/ + +#include "Weather.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "Player.h" +#include "World.h" +#include "Log.h" +#include "ObjectMgr.h" +#include "Util.h" + +/// Create the Weather object +Weather::Weather(uint32 zone, WeatherZoneChances const* weatherChances) : m_zone(zone), m_weatherChances(weatherChances) +{ + m_timer.SetInterval(sWorld.getConfig(CONFIG_INTERVAL_CHANGEWEATHER)); + m_type = WEATHER_TYPE_FINE; + m_grade = 0; + + sLog.outDetail("WORLD: Starting weather system for zone %u (change every %u minutes).", m_zone, (uint32)(m_timer.GetInterval() / (1000*MINUTE)) ); +} + +/// Launch a weather update +bool Weather::Update(time_t diff) +{ + if (m_timer.GetCurrent()>=0) + m_timer.Update(diff); + else m_timer.SetCurrent(0); + + ///- If the timer has passed, ReGenerate the weather + if(m_timer.Passed()) + { + m_timer.Reset(); + // update only if Regenerate has changed the weather + if(ReGenerate()) + { + ///- Weather will be removed if not updated (no players in zone anymore) + if(!UpdateWeather()) + return false; + } + } + return true; +} + +/// Calculate the new weather +bool Weather::ReGenerate() +{ + if (!m_weatherChances) + { + m_type = WEATHER_TYPE_FINE; + m_grade = 0.0f; + return false; + } + + /// Weather statistics: + ///- 30% - no change + ///- 30% - weather gets better (if not fine) or change weather type + ///- 30% - weather worsens (if not fine) + ///- 10% - radical change (if not fine) + uint32 u = urand(0, 99); + + if (u < 30) + return false; + + // remember old values + WeatherType old_type = m_type; + float old_grade = m_grade; + + //78 days between January 1st and March 20nd; 365/4=91 days by season + // season source http://aa.usno.navy.mil/data/docs/EarthSeasons.html + time_t gtime = sWorld.GetGameTime(); + struct tm * ltime = localtime(>ime); + uint32 season = ((ltime->tm_yday - 78 + 365)/91)%4; + + static char const* seasonName[WEATHER_SEASONS] = { "spring", "summer", "fall", "winter" }; + + sLog.outDebug("Generating a change in %s weather for zone %u.", seasonName[season], m_zone); + + if ((u < 60) && (m_grade < 0.33333334f)) // Get fair + { + m_type = WEATHER_TYPE_FINE; + m_grade = 0.0f; + } + + if ((u < 60) && (m_type != WEATHER_TYPE_FINE)) // Get better + { + m_grade -= 0.33333334f; + return true; + } + + if ((u < 90) && (m_type != WEATHER_TYPE_FINE)) // Get worse + { + m_grade += 0.33333334f; + return true; + } + + if (m_type != WEATHER_TYPE_FINE) + { + /// Radical change: + ///- if light -> heavy + ///- if medium -> change weather type + ///- if heavy -> 50% light, 50% change weather type + + if (m_grade < 0.33333334f) + { + m_grade = 0.9999f; // go nuts + return true; + } + else + { + if (m_grade > 0.6666667f) + { + // Severe change, but how severe? + uint32 rnd = urand(0,99); + if (rnd < 50) + { + m_grade -= 0.6666667f; + return true; + } + } + m_type = WEATHER_TYPE_FINE; // clear up + m_grade = 0; + } + } + + // At this point, only weather that isn't doing anything remains but that have weather data + uint32 chance1 = m_weatherChances->data[season].rainChance; + uint32 chance2 = chance1+ m_weatherChances->data[season].snowChance; + uint32 chance3 = chance2+ m_weatherChances->data[season].stormChance; + + uint32 rnd = urand(0, 99); + if(rnd <= chance1) + m_type = WEATHER_TYPE_RAIN; + else if(rnd <= chance2) + m_type = WEATHER_TYPE_SNOW; + else if(rnd <= chance3) + m_type = WEATHER_TYPE_STORM; + else + m_type = WEATHER_TYPE_FINE; + + /// New weather statistics (if not fine): + ///- 85% light + ///- 7% medium + ///- 7% heavy + /// If fine 100% sun (no fog) + + if (m_type == WEATHER_TYPE_FINE) + { + m_grade = 0.0f; + } + else if (u < 90) + { + m_grade = rand_norm() * 0.3333f; + } + else + { + // Severe change, but how severe? + rnd = urand(0, 99); + if (rnd < 50) + m_grade = rand_norm() * 0.3333f + 0.3334f; + else + m_grade = rand_norm() * 0.3333f + 0.6667f; + } + + // return true only in case weather changes + return m_type != old_type || m_grade != old_grade; +} + +void Weather::SendWeatherUpdateToPlayer(Player *player) +{ + WorldPacket data( SMSG_WEATHER, (4+4+4) ); + + data << uint32(GetWeatherState()) << (float)m_grade << uint8(0); + player->GetSession()->SendPacket( &data ); +} + +void Weather::SendFineWeatherUpdateToPlayer(Player *player) +{ + WorldPacket data( SMSG_WEATHER, (4+4+4) ); + + data << (uint32)WEATHER_STATE_FINE << (float)0.0f << uint8(0); + player->GetSession()->SendPacket( &data ); +} + +/// Send the new weather to all players in the zone +bool Weather::UpdateWeather() +{ + Player* player = sWorld.FindPlayerInZone(m_zone); + if(!player) + return false; + + ///- Send the weather packet to all players in this zone + if (m_grade >= 1) + m_grade = 0.9999f; + else if (m_grade < 0) + m_grade = 0.0001f; + + WeatherState state = GetWeatherState(); + + WorldPacket data( SMSG_WEATHER, (4+4+4) ); + data << uint32(state) << (float)m_grade << uint8(0); + player->SendMessageToSet( &data, true ); + + ///- Log the event + char const* wthstr; + switch(state) + { + case WEATHER_STATE_LIGHT_RAIN: + wthstr = "light rain"; + break; + case WEATHER_STATE_MEDIUM_RAIN: + wthstr = "medium rain"; + break; + case WEATHER_STATE_HEAVY_RAIN: + wthstr = "heavy rain"; + break; + case WEATHER_STATE_LIGHT_SNOW: + wthstr = "light snow"; + break; + case WEATHER_STATE_MEDIUM_SNOW: + wthstr = "medium snow"; + break; + case WEATHER_STATE_HEAVY_SNOW: + wthstr = "heavy snow"; + break; + case WEATHER_STATE_LIGHT_SANDSTORM: + wthstr = "light sandstorm"; + break; + case WEATHER_STATE_MEDIUM_SANDSTORM: + wthstr = "medium sandstorm"; + break; + case WEATHER_STATE_HEAVY_SANDSTORM: + wthstr = "heavy sandstorm"; + break; + case WEATHER_STATE_THUNDERS: + wthstr = "thunders"; + break; + case WEATHER_STATE_BLACKRAIN: + wthstr = "blackrain"; + break; + case WEATHER_STATE_FINE: + default: + wthstr = "fine"; + break; + } + sLog.outDetail("Change the weather of zone %u to %s.", m_zone, wthstr); + + return true; +} + +/// Set the weather +void Weather::SetWeather(WeatherType type, float grade) +{ + if(m_type == type && m_grade == grade) + return; + + m_type = type; + m_grade = grade; + UpdateWeather(); +} + +/// Get the sound number associated with the current weather +WeatherState Weather::GetWeatherState() const +{ + if (m_grade<0.27f) + return WEATHER_STATE_FINE; + + switch(m_type) + { + case WEATHER_TYPE_RAIN: + if(m_grade<0.40f) + return WEATHER_STATE_LIGHT_RAIN; + else if(m_grade<0.70f) + return WEATHER_STATE_MEDIUM_RAIN; + else + return WEATHER_STATE_HEAVY_RAIN; + case WEATHER_TYPE_SNOW: + if(m_grade<0.40f) + return WEATHER_STATE_LIGHT_SNOW; + else if(m_grade<0.70f) + return WEATHER_STATE_MEDIUM_SNOW; + else + return WEATHER_STATE_HEAVY_SNOW; + case WEATHER_TYPE_STORM: + if(m_grade<0.40f) + return WEATHER_STATE_LIGHT_SANDSTORM; + else if(m_grade<0.70f) + return WEATHER_STATE_MEDIUM_SANDSTORM; + else + return WEATHER_STATE_HEAVY_SANDSTORM; + case WEATHER_TYPE_BLACKRAIN: + return WEATHER_STATE_BLACKRAIN; + case WEATHER_TYPE_THUNDERS: + return WEATHER_STATE_THUNDERS; + case WEATHER_TYPE_FINE: + default: + return WEATHER_STATE_FINE; + } +} diff --git a/src/game/Weather.h b/src/game/Weather.h new file mode 100644 index 00000000000..e6eaa49a3e7 --- /dev/null +++ b/src/game/Weather.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup world +/// @{ +/// \file + +#ifndef __WEATHER_H +#define __WEATHER_H + +#include "Common.h" +#include "SharedDefines.h" +#include "Timer.h" + +class Player; + +enum WeatherState +{ + WEATHER_STATE_FINE = 0, + WEATHER_STATE_LIGHT_RAIN = 3, + WEATHER_STATE_MEDIUM_RAIN = 4, + WEATHER_STATE_HEAVY_RAIN = 5, + WEATHER_STATE_LIGHT_SNOW = 6, + WEATHER_STATE_MEDIUM_SNOW = 7, + WEATHER_STATE_HEAVY_SNOW = 8, + WEATHER_STATE_LIGHT_SANDSTORM = 22, + WEATHER_STATE_MEDIUM_SANDSTORM = 41, + WEATHER_STATE_HEAVY_SANDSTORM = 42, + WEATHER_STATE_THUNDERS = 86, + WEATHER_STATE_BLACKRAIN = 90 +}; + +struct WeatherZoneChances; + +/// Weather for one zone +class Weather +{ + public: + Weather(uint32 zone, WeatherZoneChances const* weatherChances); + ~Weather() { }; + bool ReGenerate(); + bool UpdateWeather(); + void SendWeatherUpdateToPlayer(Player *player); + static void SendFineWeatherUpdateToPlayer(Player *player); + void SetWeather(WeatherType type, float grade); + /// For which zone is this weather? + uint32 GetZone() { return m_zone; }; + bool Update(time_t diff); + private: + WeatherState GetWeatherState() const; + uint32 m_zone; + WeatherType m_type; + float m_grade; + IntervalTimer m_timer; + WeatherZoneChances const* m_weatherChances; +}; +#endif diff --git a/src/game/World.cpp b/src/game/World.cpp new file mode 100644 index 00000000000..8546778ae21 --- /dev/null +++ b/src/game/World.cpp @@ -0,0 +1,2460 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup world +*/ + +#include "Common.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 "WorldSocket.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_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::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; +} + +/// 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; +} + +/// Add a session to the session list +void World::AddSession(WorldSession* s) +{ + ASSERT(s); + + WorldSession* old = m_sessions[s->GetAccountId()]; + m_sessions[s->GetAccountId()] = s; + + // if session already exist, prepare to it deleting at next world update + if(old) + m_kicked_sessions.insert(old); +} + +int32 World::GetQueuePos(WorldSocket* socket) +{ + uint32 position = 1; + + for(Queue::iterator iter = m_QueuedPlayer.begin(); iter != m_QueuedPlayer.end(); ++iter, ++position) + if((*iter) == socket) + return position; + + return 0; +} + +void World::AddQueuedPlayer(WorldSocket* socket) +{ + m_QueuedPlayer.push_back(socket); +} + +void World::RemoveQueuedPlayer(WorldSocket* socket) +{ + // 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 socket to remove and count skipped positions + for(;iter != m_QueuedPlayer.end(); ++iter, ++position) + { + if(*iter==socket) + { + 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() ) + { + WorldSocket * 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_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE] = sConfig.GetBoolDefault("Battleground.QueueAnnouncer.Enable", true); + m_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY] = sConfig.GetBoolDefault("Battleground.QueueAnnouncer.PlayerOnly", false); + + 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_WISPERING_TO] = sConfig.GetBoolDefault("GM.WhisperingTo",false); + m_configs[CONFIG_GM_IN_GM_LIST] = sConfig.GetBoolDefault("GM.InGMList",false); + m_configs[CONFIG_GM_IN_WHO_LIST] = sConfig.GetBoolDefault("GM.InWhoList",false); + m_configs[CONFIG_GM_LOGIN_STATE] = sConfig.GetIntDefault("GM.LoginState",2); + m_configs[CONFIG_GM_LOG_TRADE] = sConfig.GetBoolDefault("GM.LogTrade", false); + + m_configs[CONFIG_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_DETECT_POS_COLLISION] = sConfig.GetBoolDefault("DetectPosCollision", true); + + 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_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 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::Initialize(); + Player::InitVisibleBits(); + + ///- Initialize MapManager + sLog.outString( "Starting Map System" ); + MapManager::Instance().Initialize(); + + ///- Initialize Battlegrounds + sLog.outString( "Starting BattleGround System" ); + sBattleGroundMgr.CreateInitialBattleGrounds(); + + //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() ? "" : 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; + } + + ///
  • 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); + } + } + } + } + + ///
  • Handle session updates when the timer has passed + if (m_timers[WUPDATE_SESSIONS].Passed()) + { + m_timers[WUPDATE_SESSIONS].Reset(); + + UpdateSessions(diff); + } + + ///
  • 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); + } + } + } + ///
  • 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)); + } + + ///
  • 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(); + } + + ///
+ ///- 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(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(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::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::Find(step.ownerGUID); + if(player) + source = player->GetItemByGuid(step.sourceGUID); + break; + } + case HIGHGUID_UNIT: + source = HashMapHolder::Find(step.sourceGUID); + break; + case HIGHGUID_PET: + source = HashMapHolder::Find(step.sourceGUID); + break; + case HIGHGUID_PLAYER: + source = HashMapHolder::Find(step.sourceGUID); + break; + case HIGHGUID_GAMEOBJECT: + source = HashMapHolder::Find(step.sourceGUID); + break; + case HIGHGUID_CORPSE: + source = HashMapHolder::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::Find(step.targetGUID); + break; + case HIGHGUID_PET: + target = HashMapHolder::Find(step.targetGUID); + break; + case HIGHGUID_PLAYER: // empty GUID case also + target = HashMapHolder::Find(step.targetGUID); + break; + case HIGHGUID_GAMEOBJECT: + target = HashMapHolder::Find(step.targetGUID); + break; + case HIGHGUID_CORPSE: + target = HashMapHolder::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(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(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 checker(go,go_check); + + TypeContainerVisitor, GridTypeMapContainer > object_checker(checker); + CellLock 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 checker(door,go_check); + + TypeContainerVisitor, GridTypeMapContainer > object_checker(checker); + CellLock 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 checker(door,go_check); + + TypeContainerVisitor, GridTypeMapContainer > object_checker(checker); + CellLock 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 > 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* 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 + 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 ) +{ + ///- Delete kicked sessions at add new session + for (std::set::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 new file mode 100644 index 00000000000..56450832ab9 --- /dev/null +++ b/src/game/World.h @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup world The World +/// @{ +/// \file + +#ifndef __WORLD_H +#define __WORLD_H + +#include "Common.h" +#include "Timer.h" +#include "Policies/Singleton.h" + +#include +#include +#include + +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_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE, + CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY, + CONFIG_INSTANCE_RESET_TIME_HOUR, + CONFIG_INSTANCE_UNLOAD_DELAY, + CONFIG_CAST_UNSTUCK, + CONFIG_MAX_PRIMARY_TRADE_SKILL, + CONFIG_MIN_PETITION_SIGNS, + CONFIG_GM_WISPERING_TO, + CONFIG_GM_IN_GM_LIST, + CONFIG_GM_IN_WHO_LIST, + CONFIG_GM_LOGIN_STATE, + CONFIG_GM_LOG_TRADE, + CONFIG_GROUP_VISIBILITY, + CONFIG_MAIL_DELIVERY_DELAY, + CONFIG_UPTIME_UPDATE, + CONFIG_SKILL_CHANCE_ORANGE, + CONFIG_SKILL_CHANCE_YELLOW, + CONFIG_SKILL_CHANCE_GREEN, + CONFIG_SKILL_CHANCE_GREY, + CONFIG_SKILL_CHANCE_MINING_STEPS, + CONFIG_SKILL_CHANCE_SKINNING_STEPS, + CONFIG_SKILL_PROSPECTING, + CONFIG_SKILL_GAIN_CRAFTING, + CONFIG_SKILL_GAIN_DEFENSE, + CONFIG_SKILL_GAIN_GATHERING, + CONFIG_SKILL_GAIN_WEAPON, + CONFIG_MAX_OVERSPEED_PINGS, + CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY, + CONFIG_WEATHER, + CONFIG_EXPANSION, + CONFIG_CHATFLOOD_MESSAGE_COUNT, + CONFIG_CHATFLOOD_MESSAGE_DELAY, + CONFIG_CHATFLOOD_MUTE_TIME, + CONFIG_EVENT_ANNOUNCE, + CONFIG_CREATURE_FAMILY_ASSISTEMCE_RADIUS, + CONFIG_WORLD_BOSS_LEVEL_DIFF, + CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF, + CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF, + CONFIG_DETECT_POS_COLLISION, + CONFIG_RESTRICTED_LFG_CHANNEL, + CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL, + CONFIG_TALENTS_INSPECTING, + CONFIG_CHAT_FAKE_MESSAGE_PREVENTING, + CONFIG_TCP_NO_DELAY, + CONFIG_CORPSE_DECAY_NORMAL, + CONFIG_CORPSE_DECAY_RARE, + CONFIG_CORPSE_DECAY_ELITE, + CONFIG_CORPSE_DECAY_RAREELITE, + CONFIG_CORPSE_DECAY_WORLDBOSS, + CONFIG_ADDON_CHANNEL, + CONFIG_DEATH_SICKNESS_LEVEL, + CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP, + CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE, + CONFIG_THREAT_RADIUS, + CONFIG_DECLINED_NAMES_USED, + CONFIG_LISTEN_RANGE_SAY, + CONFIG_LISTEN_RANGE_TEXTEMOTE, + CONFIG_LISTEN_RANGE_YELL, + CONFIG_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 Queue; + void AddQueuedPlayer(WorldSocket* Socket); + void RemoveQueuedPlayer(WorldSocket* Socket); + int32 GetQueuePos(WorldSocket* Socket); + 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 > const& scripts, uint32 id, Object* source, Object* target); + void ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target); + bool IsScriptScheduled() const { return !m_scriptSchedule.empty(); } + + // for max speed access + static float GetMaxVisibleDistanceForCreature() { return m_MaxVisibleDistanceForCreature; } + static float GetMaxVisibleDistanceForPlayer() { return m_MaxVisibleDistanceForPlayer; } + static float GetMaxVisibleDistanceForObject() { return m_MaxVisibleDistanceForObject; } + static float GetMaxVisibleDistanceInFlight() { return m_MaxVisibleDistanceInFlight; } + static float GetVisibleUnitGreyDistance() { return m_VisibleUnitGreyDistance; } + static float GetVisibleObjectGreyDistance() { return m_VisibleObjectGreyDistance; } + + void ProcessCliCommands(); + void QueueCliCommand(CliCommandHolder* command) { cliCmdQueue.add(command); } + + void UpdateResultQueue(); + void InitResultQueue(); + + void UpdateRealmCharCount(uint32 accid); + + LocaleConstant GetAvailableDbcLocale(LocaleConstant locale) const { if(m_availableDbcLocaleMask & (1 << locale)) return locale; else return m_defaultDbcLocale; } + protected: + void _UpdateGameTime(); + void ScriptsProcess(); + // callback for UpdateRealmCharacters + void _UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId); + + void InitDailyQuestResetTime(); + void ResetDailyQuests(); + private: + time_t m_startTime; + time_t m_gameTime; + IntervalTimer m_timers[WUPDATE_COUNT]; + uint32 mail_timer; + uint32 mail_timer_expires; + + typedef HM_NAMESPACE::hash_map WeatherMap; + WeatherMap m_weathers; + typedef HM_NAMESPACE::hash_map SessionMap; + SessionMap m_sessions; + std::set m_kicked_sessions; + uint32 m_maxActiveSessionCount; + uint32 m_maxQueuedSessionCount; + + std::multimap m_scriptSchedule; + + float rate_values[MAX_RATES]; + uint32 m_configs[CONFIG_VALUE_COUNT]; + int32 m_playerLimit; + LocaleConstant m_defaultDbcLocale; // from config for one from loaded DBC locales + uint32 m_availableDbcLocaleMask; // by loaded DBC + void DetectDBCLang(); + bool m_allowMovement; + std::string m_motd; + std::string m_dataPath; + + uint32 m_ShutdownTimer; + uint32 m_ShutdownMask; + + // for max speed access + static float m_MaxVisibleDistanceForCreature; + static float m_MaxVisibleDistanceForPlayer; + static float m_MaxVisibleDistanceForObject; + static float m_MaxVisibleDistanceInFlight; + static float m_VisibleUnitGreyDistance; + static float m_VisibleObjectGreyDistance; + + // CLI command holder to be thread safe + ZThread::LockedQueue cliCmdQueue; + SqlResultQueue *m_resultQueue; + + // next daily quests reset time + time_t m_NextDailyQuestReset; + + //Player Queue + Queue m_QueuedPlayer; +}; + +extern uint32 realmID; + +#define sWorld MaNGOS::Singleton::Instance() +#endif +/// @} diff --git a/src/game/WorldLog.cpp b/src/game/WorldLog.cpp new file mode 100644 index 00000000000..21fc26da41b --- /dev/null +++ b/src/game/WorldLog.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup u2w +*/ + +#include "WorldLog.h" +#include "Policies/SingletonImp.h" +#include "Config/ConfigEnv.h" + +#define CLASS_LOCK MaNGOS::ClassLevelLockable +INSTANTIATE_SINGLETON_2(WorldLog, CLASS_LOCK); +INSTANTIATE_CLASS_MUTEX(WorldLog, ZThread::FastMutex); + +#define WORLD_LOG_FILE_STRING "world.log" + +/// Open the log file (if specified so in the configuration file) +void WorldLog::Initialize() +{ + std::string logsDir = sConfig.GetStringDefault("LogsDir",""); + + if(!logsDir.empty()) + { + if((logsDir.at(logsDir.length()-1)!='/') && (logsDir.at(logsDir.length()-1)!='\\')) + logsDir.append("/"); + } + + std::string logname = sConfig.GetStringDefault("WorldLogFile", ""); + if(!logname.empty()) + { + i_file = fopen((logsDir+logname).c_str(), "w"); + } +} + +#define sWorldLog WorldLog::Instance() diff --git a/src/game/WorldLog.h b/src/game/WorldLog.h new file mode 100644 index 00000000000..5047161b63e --- /dev/null +++ b/src/game/WorldLog.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup u2w +/// @{ +/// \file + +#ifndef MANGOS_WORLDLOG_H +#define MANGOS_WORLDLOG_H + +#include "Common.h" +#include "Policies/Singleton.h" +#include "Errors.h" + +#include + +/// %Log packets to a file +class MANGOS_DLL_DECL WorldLog : public MaNGOS::Singleton > +{ + friend class MaNGOS::OperatorNew; + WorldLog() : i_file(NULL) { Initialize(); } + WorldLog(const WorldLog &); + WorldLog& operator=(const WorldLog &); + typedef MaNGOS::ClassLevelLockable::Lock Guard; + + /// Close the file in destructor + ~WorldLog() + { + if( i_file != NULL ) + fclose(i_file); + i_file = NULL; + } + + public: + void Initialize(); + /// Is the world logger active? + inline bool LogWorld(void) const { return (i_file != NULL); } + /// %Log to the file + inline void Log(char const *fmt, ...) + { + if( LogWorld() ) + { + Guard guard(*this); + ASSERT(i_file); + + va_list args; + va_start(args, fmt); + vfprintf(i_file, fmt, args); + va_end(args); + + fflush(i_file); + } + } + + private: + FILE *i_file; +}; + +#define sWorldLog WorldLog::Instance() +#endif +/// @} diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp new file mode 100644 index 00000000000..eaba12b4a10 --- /dev/null +++ b/src/game/WorldSession.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup u2w +*/ + +#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), _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) +{ +} + +/// WorldSession destructor +WorldSession::~WorldSession() +{ + ///- unload player if not unloaded + if(_player) + LogoutPlayer(true); + + /// - If have unclosed socket, close it + if(_socket) + _socket->CloseSocket(); + + _socket = NULL; + + ///- empty incoming packet queue + while(!_recvQueue.empty()) + { + WorldPacket *packet = _recvQueue.next(); + delete packet; + } +} + +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() : ""; +} + +/// Set the WorldSocket associated with this session +void WorldSession::SetSocket(WorldSocket *sock) +{ + _socket = sock; +} + +/// Send a packet to the client +void WorldSession::SendPacket(WorldPacket const* packet) +{ + if (!_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 + + _socket->SendPacket(packet); +} + +/// Add an incoming packet to the queue +void WorldSession::QueuePacket(WorldPacket& packet) +{ + WorldPacket *pck = new WorldPacket(packet); + _recvQueue.add(pck); +} + +/// 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*/) +{ + 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 (!_socket || (ShouldLogOut(currTime) && !m_playerLoading)) + LogoutPlayer(true); + + if (!_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 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::const_iterator itr = aset.begin(); itr != aset.end(); ++itr) + (*itr)->RewardHonor(_player,aset.size()); + + // give bg rewards and update counters like kill by first from attackers + // this can't be called for all attackers. + if(!aset.empty()) + if(BattleGround *bg = _player->GetBattleGround()) + bg->HandleKillPlayer(_player,*aset.begin()); + } + else if(_player->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION)) + { + // this will kill character by SPELL_AURA_SPIRIT_OF_REDEMPTION + _player->RemoveSpellsCausingAura(SPELL_AURA_MOD_SHAPESHIFT); + //_player->SetDeathPvP(*); set at SPELL_AURA_SPIRIT_OF_REDEMPTION apply time + _player->KillPlayer(); + _player->BuildPlayerRepop(); + _player->RepopAtGraveyard(); + } + + ///- Remove player from battleground (teleport to entrance) + if(_player->InBattleGround()) + _player->LeaveBattleground(); + + for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + { + if(int32 bgTypeId = _player->GetBattleGroundQueueId(i)) + { + _player->RemoveBattleGroundQueueId(bgTypeId); + sBattleGroundMgr.m_BattleGroundQueues[ bgTypeId ].RemovePlayer(_player->GetGUID(), true); + } + } + + ///- Reset the online field in the account table + // no point resetting online in character table here as Player::SaveToDB() will set it to 1 since player has not been removed from world at this stage + //No SQL injection as AccountID is uint32 + loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = '%u'", GetAccountId()); + + ///- If the player is in a guild, update the guild roster and broadcast a logout message to other guild members + Guild *guild = objmgr.GetGuildById(_player->GetGuildId()); + if(guild) + { + guild->LoadPlayerStatsByGuid(_player->GetGUID()); + guild->UpdateLogoutTime(_player->GetGUID()); + + WorldPacket data(SMSG_GUILD_EVENT, (1+1+12+8)); // name limited to 12 in character table. + data<<(uint8)GE_SIGNED_OFF; + data<<(uint8)1; + data<<_player->GetName(); + data<<_player->GetGUID(); + guild->BroadcastPacket(&data); + } + + ///- Remove pet + _player->RemovePet(NULL,PET_SAVE_AS_CURRENT, true); + + ///- empty buyback items and save the player in the database + // some save parts only correctly work in case player present in map/player_lists (pets, etc) + if(Save) + { + uint32 eslot; + for(int j = BUYBACK_SLOT_START; j < BUYBACK_SLOT_END; j++) + { + eslot = j - BUYBACK_SLOT_START; + _player->SetUInt64Value(PLAYER_FIELD_VENDORBUYBACK_SLOT_1+eslot*2,0); + _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1+eslot,0); + _player->SetUInt32Value(PLAYER_FIELD_BUYBACK_TIMESTAMP_1+eslot,0); + } + _player->SaveToDB(); + } + + ///- Leave all channels before player delete... + _player->CleanupChannels(); + + ///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group. + _player->UninviteFromGroup(); + + // remove player from the group if he is: + // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected) + if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && _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(!_socket) + return; + + // player will be logout and session will removed in next update tick + _socket->CloseSocket(); + _socket = NULL; +} + +/// Cancel channeling handler + +void WorldSession::SendAreaTriggerMessage(const char* Text, ...) +{ + va_list ap; + char szStr [1024]; + szStr[0] = '\0'; + + va_start(ap, Text); + vsnprintf( szStr, 1024, Text, ap ); + va_end(ap); + + uint32 length = strlen(szStr)+1; + WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length); + data << length; + data << szStr; + SendPacket(&data); +} + +void WorldSession::SendNotification(const char *format,...) +{ + if(format) + { + va_list ap; + char szStr [1024]; + szStr[0] = '\0'; + va_start(ap, format); + vsnprintf( szStr, 1024, format, ap ); + va_end(ap); + + WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1)); + data << szStr; + SendPacket(&data); + } +} + +const char * WorldSession::GetMangosString( int32 entry ) +{ + return objmgr.GetMangosString(entry,GetSessionDbLocaleIndex()); +} + +void WorldSession::Handle_NULL( WorldPacket& recvPacket ) +{ + sLog.outError( "SESSION: received unhandled opcode %s (0x%.4X)", + LookupOpcodeName(recvPacket.GetOpcode()), + recvPacket.GetOpcode()); +} + +void WorldSession::Handle_EarlyProccess( WorldPacket& recvPacket ) +{ + sLog.outError( "SESSION: received opcode %s (0x%.4X) that must be proccessed in WorldSocket::OnRead", + LookupOpcodeName(recvPacket.GetOpcode()), + recvPacket.GetOpcode()); +} + +void WorldSession::Handle_ServerSide( WorldPacket& recvPacket ) +{ + sLog.outError( "SESSION: received sever-side opcode %s (0x%.4X)", + LookupOpcodeName(recvPacket.GetOpcode()), + recvPacket.GetOpcode()); +} + +void WorldSession::Handle_Depricated( WorldPacket& recvPacket ) +{ + sLog.outError( "SESSION: received depricated opcode %s (0x%.4X)", + LookupOpcodeName(recvPacket.GetOpcode()), + recvPacket.GetOpcode()); +} diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h new file mode 100644 index 00000000000..05df0afe3a8 --- /dev/null +++ b/src/game/WorldSession.h @@ -0,0 +1,638 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup u2w +/// @{ +/// \file + +#ifndef __WORLDSESSION_H +#define __WORLDSESSION_H + +#include "Common.h" + +class MailItemsInfo; +struct ItemPrototype; +struct AuctionEntry; + +class Creature; +class Item; +class Object; +class Player; +class Unit; +class WorldPacket; +class WorldSocket; +class WorldSession; +class QueryResult; +class LoginQueryHolder; +class CharacterHandler; + +#define CHECK_PACKET_SIZE(P,S) if((P).size() < (S)) return SizeError((P),(S)); + +enum PartyOperation +{ + PARTY_OP_INVITE = 0, + PARTY_OP_LEAVE = 2 +}; + +enum PartyResult +{ + PARTY_RESULT_OK = 0, + PARTY_RESULT_CANT_FIND_TARGET = 1, + PARTY_RESULT_NOT_IN_YOUR_PARTY = 2, + PARTY_RESULT_NOT_IN_YOUR_INSTANCE = 3, + PARTY_RESULT_PARTY_FULL = 4, + PARTY_RESULT_ALREADY_IN_GROUP = 5, + PARTY_RESULT_YOU_NOT_IN_GROUP = 6, + PARTY_RESULT_YOU_NOT_LEADER = 7, + PARTY_RESULT_TARGET_UNFRIENDLY = 8, + PARTY_RESULT_TARGET_IGNORE_YOU = 9, + PARTY_RESULT_INVITE_RESTRICTED = 13 +}; + +/// Player session in the World +class MANGOS_DLL_SPEC WorldSession +{ + friend class CharacterHandler; + public: + WorldSession(uint32 id, WorldSocket *sock, uint32 sec, bool tbc, time_t mute_time, LocaleConstant locale); + ~WorldSession(); + + bool PlayerLoading() const { return m_playerLoading; } + bool PlayerLogout() const { return m_playerLogout; } + + void SizeError(WorldPacket const& packet, uint32 size) const; + + void SendPacket(WorldPacket const* packet); + void SendNotification(const char *format,...) ATTR_PRINTF(2,3); + void SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type); + void SendPartyResult(PartyOperation operation, std::string member, PartyResult res); + void SendAreaTriggerMessage(const char* Text, ...) ATTR_PRINTF(2,3); + + uint32 GetSecurity() const { return _security; } + uint32 GetAccountId() const { return _accountId; } + Player* GetPlayer() const { return _player; } + char const* GetPlayerName() const; + void SetSecurity(uint32 security) { _security = security; } + void SetSocket(WorldSocket *sock); + 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& packet); + bool Update(uint32 diff); + + //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 *_socket; + + 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 _recvQueue; +}; +#endif +/// @} diff --git a/src/game/WorldSocket.cpp b/src/game/WorldSocket.cpp new file mode 100644 index 00000000000..d52c0962862 --- /dev/null +++ b/src/game/WorldSocket.cpp @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup u2w +*/ + +#include "Common.h" +#include "Log.h" +#include "Opcodes.h" +#include "Database/DatabaseEnv.h" +#include "Auth/Sha1.h" +#include "WorldPacket.h" +#include "WorldSocket.h" +#include "WorldSession.h" +#include "World.h" +#include "WorldSocketMgr.h" +#include "Policies/SingletonImp.h" +#include "WorldLog.h" +#include "AddonHandler.h" +#include "sockets/Utility.h" +#include "Util.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 + +/// Client Packet Header +struct ClientPktHeader +{ + uint16 size; + uint32 cmd; +}; + +/// Server Packet Header +struct ServerPktHeader +{ + uint16 size; + uint16 cmd; +}; + +// 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 + +#define SOCKET_CHECK_PACKET_SIZE(P,S) if((P).size() < (S)) return SizeError((P),(S)); + +/// WorldSocket construction and initialization. +WorldSocket::WorldSocket(ISocketHandler &sh): TcpSocket(sh), _cmd(0), _remaining(0), _session(NULL) +{ + _seed = static_cast(rand32()); + m_LastPingMSTime = 0; // first time it will counted as overspeed maybe, but this is not important + m_OverSpeedPings = 0; + + if (sWorld.getConfig(CONFIG_TCP_NO_DELAY)) + SetTcpNodelay(true); +} + +/// WorldSocket destructor +WorldSocket::~WorldSocket() +{ + if(_session) + _session->SetSocket(0); + + WorldPacket *packet; + + ///- Go through the to-be-sent queue and delete remaining packets + while(!_sendQueue.empty()) + { + packet = _sendQueue.next(); + delete packet; + } +} + +/// Copy the packet to the to-be-sent queue +void WorldSocket::SendPacket(WorldPacket const* packet) +{ + WorldPacket *pck = new WorldPacket(*packet); + ASSERT(pck); + _sendQueue.add(pck); +} + +/// On client connection +void WorldSocket::OnAccept() +{ + ///- Add the current socket to the list of sockets to be managed (WorldSocketMgr) + sWorldSocketMgr.AddSocket(this); + Utility::ResolveLocal(); + + ///- Send a AUTH_CHALLENGE packet + WorldPacket packet( SMSG_AUTH_CHALLENGE, 4 ); + packet << _seed; + + SendPacket(&packet); +} + +/// Read the client transmitted data +void WorldSocket::OnRead() +{ + TcpSocket::OnRead(); + + while(1) + { + ///- Read the packet header and decipher it (if needed) + if (!_remaining) + { + if (ibuf.GetLength() < 6) + break; + + ClientPktHeader hdr; + + ibuf.Read((char *)&hdr, 6); + _crypt.DecryptRecv((uint8 *)&hdr, 6); + + _remaining = ntohs(hdr.size) - 4; + _cmd = hdr.cmd; + } + + if (ibuf.GetLength() < _remaining) + break; + + ///- Read the remaining of the packet + WorldPacket packet((uint16)_cmd, _remaining); + + packet.resize(_remaining); + if(_remaining) ibuf.Read((char*)packet.contents(), _remaining); + _remaining = 0; + + ///- If log of world packets is enable, log the incoming packet + if( sWorldLog.LogWorld() ) + { + sWorldLog.Log("CLIENT:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n", + (uint32)GetSocket(), + packet.size(), + LookupOpcodeName(packet.GetOpcode()), + packet.GetOpcode()); + + uint32 p = 0; + while (p < packet.size()) + { + for (uint32 j = 0; j < 16 && p < packet.size(); j++) + sWorldLog.Log("%.2X ", packet[p++]); + sWorldLog.Log("\n"); + } + sWorldLog.Log("\n\n"); + } + + ///- If the packet is PING, KEEP_ALIVE or AUTH_SESSION, handle immediately + switch (_cmd) + { + case CMSG_KEEP_ALIVE: + break; // just ignore, network connectivity timeout preventing + case CMSG_PING: + { + _HandlePing(packet); + break; + } + case CMSG_AUTH_SESSION: + { + _HandleAuthSession(packet); + break; + } + default: + { + ///- Else, put it in the world session queue for this user (need to be already authenticated) + if (_session) + _session->QueuePacket(packet); + else + sLog.outDetail("Received out of place packet with cmdid 0x%.4X", _cmd); + break; + } + } + } +} + +/// On socket closing +void WorldSocket::CloseSocket() +{ + ///- Set CloseAndDelete flag for TcpSocket class + SetCloseAndDelete(true); + + ///- Set _session to NULL. Prevent crashes + _session = NULL; +} + +/// On socket deleting +void WorldSocket::OnDelete() +{ + ///- Stop sending remaining data through this socket + if (_session) + { + _session->SetSocket(NULL); + // Session deleted from World session list at socket==0, This is only back reference from socket to session. + _session = NULL; + } + + ///- Remove the socket from the WorldSocketMgr list + sWorldSocketMgr.RemoveSocket(this); + + ///- Removes socket from player queue + sWorld.RemoveQueuedPlayer(this); +} + +/// Handle the client authentication packet +void WorldSocket::_HandleAuthSession(WorldPacket& recvPacket) +{ + uint8 digest[20]; + uint32 clientSeed; + uint32 unk2; + uint32 BuiltNumberClient; + uint32 id, security; + bool tbc = false; + std::string account; + Sha1Hash sha1; + BigNumber v, s, g, N, x, I; + WorldPacket packet, SendAddonPacked; + + BigNumber K; + + SOCKET_CHECK_PACKET_SIZE(recvPacket,4+4+1+4+20); + + ///- Read the content of the packet + recvPacket >> BuiltNumberClient; // for now no use + recvPacket >> unk2; + recvPacket >> account; + + // recheck size + SOCKET_CHECK_PACKET_SIZE(recvPacket,4+4+(account.size()+1)+4+20); + + recvPacket >> clientSeed; + recvPacket.read(digest, 20); + + sLog.outDebug("Auth: client %u, unk2 %u, account %s, clientseed %u", BuiltNumberClient, unk2, account.c_str(), clientSeed); + + ///- Normalize account name + //utf8ToUpperOnlyLatin(account); -- client already send account in expected form + + ///- Get the account information from the realmd database + std::string safe_account = account; // Duplicate, else will screw the SHA hash verification below + loginDatabase.escape_string(safe_account); + //No SQL injection, username escaped. + // 0 1 2 3 4 5 6 7 8 9 10 + QueryResult *result = loginDatabase.PQuery("SELECT id, gmlevel, sessionkey, last_ip, locked, sha_pass_hash, v, s, tbc, mutetime, locale FROM account WHERE username = '%s'", safe_account.c_str()); + + ///- Stop if the account is not found + if ( !result ) + { + packet.Initialize( SMSG_AUTH_RESPONSE, 1 ); + packet << uint8( AUTH_UNKNOWN_ACCOUNT ); + SendPacket( &packet ); + sLog.outDetail( "SOCKET: Sent Auth Response (unknown account)." ); + return; + } + + Field* fields = result->Fetch(); + + tbc = fields[8].GetUInt8() && sWorld.getConfig(CONFIG_EXPANSION) > 0; + + N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); + g.SetDword(7); + I.SetHexStr(fields[5].GetString()); + + //In case of leading zeros in the I hash, restore them + uint8 mDigest[SHA_DIGEST_LENGTH]; + memset(mDigest,0,SHA_DIGEST_LENGTH); + if (I.GetNumBytes() <= SHA_DIGEST_LENGTH) + memcpy(mDigest,I.AsByteArray(),I.GetNumBytes()); + + std::reverse(mDigest,mDigest+SHA_DIGEST_LENGTH); + + s.SetHexStr(fields[7].GetString()); + sha1.UpdateData(s.AsByteArray(), s.GetNumBytes()); + sha1.UpdateData(mDigest, SHA_DIGEST_LENGTH); + sha1.Finalize(); + x.SetBinary(sha1.GetDigest(), sha1.GetLength()); + v = g.ModExp(x, N); + + const char* sStr = s.AsHexStr(); //Must be freed by OPENSSL_free() + const char* vStr = v.AsHexStr(); //Must be freed by OPENSSL_free() + const char* vold = fields[6].GetString(); + sLog.outDebug("SOCKET: (s,v) check s: %s v_old: %s v_new: %s", sStr, vold, vStr ); + loginDatabase.PExecute("UPDATE account SET v = '0', s = '0' WHERE username = '%s'", safe_account.c_str()); + if ( !vold || strcmp( vStr, vold ) ) + { + packet.Initialize( SMSG_AUTH_RESPONSE, 1 ); + packet << uint8( AUTH_UNKNOWN_ACCOUNT ); + SendPacket( &packet ); + sLog.outDetail( "SOCKET: User not logged."); + delete result; + OPENSSL_free((void*)sStr); + OPENSSL_free((void*)vStr); + return; + } + OPENSSL_free((void*)sStr); + OPENSSL_free((void*)vStr); + + ///- Re-check ip locking (same check as in realmd). + if(fields[4].GetUInt8() == 1) // if ip is locked + { + if ( strcmp(fields[3].GetString(),GetRemoteAddress().c_str()) ) + { + packet.Initialize( SMSG_AUTH_RESPONSE, 1 ); + packet << uint8( AUTH_FAILED ); + SendPacket( &packet ); + + sLog.outDetail( "SOCKET: Sent Auth Response (Account IP differs)." ); + delete result; + return; + } + } + + id = fields[0].GetUInt32(); + security = fields[1].GetUInt16(); + K.SetHexStr(fields[2].GetString()); + time_t mutetime = time_t(fields[9].GetUInt64()); + + LocaleConstant locale = LocaleConstant(fields[10].GetUInt8()); + if (locale>=MAX_LOCALE) + locale=LOCALE_enUS; + + delete result; + + ///- Re-check account ban (same check as in realmd) /// TO DO: why on earth do 2 checks for same thing? + QueryResult *banresult = loginDatabase.PQuery("SELECT bandate,unbandate FROM account_banned WHERE id = '%u' AND active = 1", id); + if(banresult) // if account banned + { + packet.Initialize( SMSG_AUTH_RESPONSE, 1 ); + packet << uint8( AUTH_BANNED ); + SendPacket( &packet ); + + sLog.outDetail( "SOCKET: Sent Auth Response (Account banned)." ); + delete banresult; + return; + } + + ///- Check locked state for server + AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit(); + if( allowedAccountType > SEC_PLAYER && security < allowedAccountType) + { + WorldPacket Packet(SMSG_AUTH_RESPONSE, 1); + Packet << uint8(AUTH_UNAVAILABLE); + SendPacket(&Packet); + return; + } + + ///- kick already loaded player with same account (if any) and remove session + ///- if player is in loading and want to load again, return + if(!sWorld.RemoveSession(id)) + { + return; + } + + ///- Check that Key and account name are the same on client and server + Sha1Hash sha; + + uint32 t = 0; + uint32 seed = _seed; + + sha.UpdateData(account); + sha.UpdateData((uint8 *)&t, 4); + sha.UpdateData((uint8 *)&clientSeed, 4); + sha.UpdateData((uint8 *)&seed, 4); + sha.UpdateBigNumbers(&K, NULL); + sha.Finalize(); + + if (memcmp(sha.GetDigest(), digest, 20)) + { + packet.Initialize( SMSG_AUTH_RESPONSE, 1 ); + packet << uint8( AUTH_FAILED ); + SendPacket( &packet ); + + sLog.outDetail( "SOCKET: Sent Auth Response (authentication failed)." ); + return; + } + + ///- Initialize the encryption with the Key + _crypt.SetKey(&K); + _crypt.Init(); + + ///- Send 'Auth is ok' + packet.Initialize( SMSG_AUTH_RESPONSE, 1+4+1+4+1 ); + packet << uint8( AUTH_OK ); + packet << uint32(0); // unknown random value... + packet << uint8(0); // can be 0 and 2 + packet << uint32(0); // const 0 + packet << uint8(tbc ? 1 : 0); // 0 - normal, 1 - TBC, must be set in database manually for each account + SendPacket(&packet); + + ///- Create a new WorldSession for the player and add it to the World + _session = new WorldSession(id, this,security,tbc,mutetime,locale); + sWorld.AddSession(_session); + + if(sLog.IsOutDebug()) // optimize disabled debug output + { + sLog.outDebug( "SOCKET: Client '%s' authenticated successfully.", account.c_str() ); + sLog.outDebug( "Account: '%s' Logged in from IP %s.", account.c_str(), GetRemoteAddress().c_str()); + } + + ///- Update the last_ip in the database + //No SQL injection, username escaped. + std::string address = GetRemoteAddress(); + loginDatabase.escape_string(address); + loginDatabase.PExecute("UPDATE account SET last_ip = '%s' WHERE username = '%s'",address.c_str(), safe_account.c_str()); + + // do small delay (10ms) at accepting successful authed connection to prevent dropping packets by client + // don't must harm anyone (let login ~100 accounts in 1 sec ;) ) + #ifdef WIN32 + Sleep(10); + #else + ZThread::Thread::sleep(10); + #endif + + ///- Check that we do not exceed the maximum number of online players in the realm + uint32 Sessions = sWorld.GetActiveAndQueuedSessionCount(); + uint32 pLimit = sWorld.GetPlayerAmountLimit(); + uint32 QueueSize = sWorld.GetQueueSize(); //number of players in the queue + bool inQueue = false; + --Sessions; //so we don't count the user trying to login as a session and queue the socket that we are using + + if( pLimit > 0 && Sessions >= pLimit && security == SEC_PLAYER ) + { + sWorld.AddQueuedPlayer(this); + SendAuthWaitQue(sWorld.GetQueuePos(this)); + sWorld.UpdateMaxSessionCounters(); + sLog.outDetail( "PlayerQueue: %s is in Queue Position (%u).",safe_account.c_str(),++QueueSize); + inQueue = true; + } + + ///- Create and send the Addon packet + if(sAddOnHandler.BuildAddonPacket(&recvPacket, &SendAddonPacked)) + SendPacket(&SendAddonPacked); + + if(inQueue) + return; + + sWorld.UpdateMaxSessionCounters(); + + // Updates the population + if (pLimit > 0) + { + float popu = sWorld.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); + } + + return; +} + +/// Handle the Ping packet +void WorldSocket::_HandlePing(WorldPacket& recvPacket) +{ + uint32 ping; + uint32 latency; + + CHECK_PACKET_SIZE(recvPacket,8); + + ///- Get the ping packet content + recvPacket >> ping; + recvPacket >> latency; + + if (_session ) + _session->SetLatency(latency); + + ///- check ping speed for players + if(_session && _session->GetSecurity() == SEC_PLAYER) + { + uint32 cur_mstime = getMSTime(); + + // can overflow and start from 0 + uint32 diff_mstime = getMSTimeDiff(m_LastPingMSTime,cur_mstime); + m_LastPingMSTime = cur_mstime; + if(diff_mstime < 27000) // should be 30000 (=30 secs), add little tolerance + { + ++m_OverSpeedPings; + + uint32 max_count = sWorld.getConfig(CONFIG_MAX_OVERSPEED_PINGS); + if(max_count && m_OverSpeedPings > max_count) + { + sLog.outBasic("Player %s from account id %u kicked for overspeed ping packets from client (non-playable connection lags or cheating) ",_session->GetPlayerName(),_session->GetAccountId()); + _session->KickPlayer(); + return; + } + } + else + m_OverSpeedPings = 0; + + } + + ///- And put the pong answer in the to-be-sent queue + WorldPacket packet( SMSG_PONG, 4 ); + packet << ping; + SendPacket(&packet); + + return; +} + +/// Handle the update order for the socket +void WorldSocket::SendSinglePacket() +{ + WorldPacket *packet; + ServerPktHeader hdr; + + ///- If we have packet to send + if (!_sendQueue.empty()) + { + packet = _sendQueue.next(); + + hdr.size = ntohs((uint16)packet->size() + 2); + hdr.cmd = packet->GetOpcode(); + + if( sWorldLog.LogWorld() ) + { + sWorldLog.Log("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n", + (uint32)GetSocket(), + packet->size(), + LookupOpcodeName(packet->GetOpcode()), + packet->GetOpcode()); + + uint32 p = 0; + while (p < packet->size()) + { + for (uint32 j = 0; j < 16 && p < packet->size(); j++) + sWorldLog.Log("%.2X ", (*packet)[p++]); + + sWorldLog.Log("\n"); + } + + sWorldLog.Log("\n\n"); + } + + ///- Encrypt (if needed) the header + _crypt.EncryptSend((uint8*)&hdr, 4); + + ///- Send the header and body to the client + TcpSocket::SendBuf((char*)&hdr, 4); + if(!packet->empty()) TcpSocket::SendBuf((char*)packet->contents(), packet->size()); + + delete packet; + } +} + +void WorldSocket::Update(time_t diff) +{ + const uint32 SEND_PACKETS_MAX = 100; + const uint32 SEND_BUFFER_SIZE = 1024; + + uint8 sendBuffer[SEND_BUFFER_SIZE]; + + while (!_sendQueue.empty()) + { + bool haveBigPacket = false; + uint32 bufferSize = 0; + + ///- While we have packets to send + for (uint32 packetCount = 0; (packetCount < SEND_PACKETS_MAX) && !_sendQueue.empty(); packetCount++) + { + ServerPktHeader *hdr = (ServerPktHeader*)&sendBuffer[bufferSize]; + + // check merge possibility. + WorldPacket *front = _sendQueue.front(); + uint32 packetSize = front->size(); + + if ((sizeof(*hdr) + packetSize) > SEND_BUFFER_SIZE) + { + haveBigPacket = true; + break; + } + + if ((bufferSize + sizeof(*hdr) + packetSize) > sizeof(sendBuffer)) + break; + + // can be merged + WorldPacket *packet = _sendQueue.next(); + + hdr->size = ntohs((uint16)packetSize + 2); + hdr->cmd = packet->GetOpcode(); + + if( sWorldLog.LogWorld() ) + { + sWorldLog.Log("SERVER:\nSOCKET: %u\nLENGTH: %u\nOPCODE: %s (0x%.4X)\nDATA:\n", + (uint32)GetSocket(), + packetSize, + LookupOpcodeName(packet->GetOpcode()), + packet->GetOpcode()); + + uint32 p = 0; + while (p < packetSize) + { + for (uint32 j = 0; j < 16 && p < packetSize; j++) + sWorldLog.Log("%.2X ", (*packet)[p++]); + + sWorldLog.Log("\n"); + } + + sWorldLog.Log("\n\n"); + } + + ///- Encrypt (if needed) the header + _crypt.EncryptSend((uint8*)hdr, sizeof(*hdr)); + bufferSize += sizeof(*hdr); + + if (packetSize) + { + memcpy(&sendBuffer[bufferSize], packet->contents(), packetSize); + bufferSize += packetSize; + } + + ///- Send the header and body to the client + delete packet; + } + + // send merged packets + if (bufferSize) TcpSocket::SendBuf((char*)sendBuffer, bufferSize); + // send too big non-merged packet + if (haveBigPacket) SendSinglePacket(); + } +} + +/// Handle the authentication waiting queue (to be completed) +void WorldSocket::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); //amount of players in queue + SendPacket(&packet); + } +} + +void WorldSocket::SizeError(WorldPacket const& packet, uint32 size) const +{ + sLog.outError("Client send packet %s (%u) with size %u but expected %u (attempt crash server?), skipped", + LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size); +} diff --git a/src/game/WorldSocket.h b/src/game/WorldSocket.h new file mode 100644 index 00000000000..26f8fb882f0 --- /dev/null +++ b/src/game/WorldSocket.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup u2w +/// @{ +/// \file + +#ifndef __WORLDSOCKET_H +#define __WORLDSOCKET_H + +#include "sockets/TcpSocket.h" +#include "Auth/AuthCrypt.h" + +enum ResponseCodes +{ + RESPONSE_SUCCESS = 0x00, + RESPONSE_FAILURE = 0x01, + RESPONSE_CANCELLED = 0x02, + RESPONSE_DISCONNECTED = 0x03, + RESPONSE_FAILED_TO_CONNECT = 0x04, + RESPONSE_CONNECTED = 0x05, + RESPONSE_VERSION_MISMATCH = 0x06, + + CSTATUS_CONNECTING = 0x07, + CSTATUS_NEGOTIATING_SECURITY = 0x08, + CSTATUS_NEGOTIATION_COMPLETE = 0x09, + CSTATUS_NEGOTIATION_FAILED = 0x0A, + CSTATUS_AUTHENTICATING = 0x0B, + + AUTH_OK = 0x0C, + AUTH_FAILED = 0x0D, + AUTH_REJECT = 0x0E, + AUTH_BAD_SERVER_PROOF = 0x0F, + AUTH_UNAVAILABLE = 0x10, + AUTH_SYSTEM_ERROR = 0x11, + AUTH_BILLING_ERROR = 0x12, + AUTH_BILLING_EXPIRED = 0x13, + AUTH_VERSION_MISMATCH = 0x14, + AUTH_UNKNOWN_ACCOUNT = 0x15, + AUTH_INCORRECT_PASSWORD = 0x16, + AUTH_SESSION_EXPIRED = 0x17, + AUTH_SERVER_SHUTTING_DOWN = 0x18, + AUTH_ALREADY_LOGGING_IN = 0x19, + AUTH_LOGIN_SERVER_NOT_FOUND = 0x1A, + AUTH_WAIT_QUEUE = 0x1B, + AUTH_BANNED = 0x1C, + AUTH_ALREADY_ONLINE = 0x1D, + AUTH_NO_TIME = 0x1E, + AUTH_DB_BUSY = 0x1F, + AUTH_SUSPENDED = 0x20, + AUTH_PARENTAL_CONTROL = 0x21, + AUTH_LOCKED_ENFORCED = 0x22, + + REALM_LIST_IN_PROGRESS = 0x23, + REALM_LIST_SUCCESS = 0x24, + REALM_LIST_FAILED = 0x25, + REALM_LIST_INVALID = 0x26, + REALM_LIST_REALM_NOT_FOUND = 0x27, + + ACCOUNT_CREATE_IN_PROGRESS = 0x28, + ACCOUNT_CREATE_SUCCESS = 0x29, + ACCOUNT_CREATE_FAILED = 0x2A, + + CHAR_LIST_RETRIEVING = 0x2B, + CHAR_LIST_RETRIEVED = 0x2C, + CHAR_LIST_FAILED = 0x2D, + + CHAR_CREATE_IN_PROGRESS = 0x2E, + CHAR_CREATE_SUCCESS = 0x2F, + CHAR_CREATE_ERROR = 0x30, + CHAR_CREATE_FAILED = 0x31, + CHAR_CREATE_NAME_IN_USE = 0x32, + CHAR_CREATE_DISABLED = 0x33, + CHAR_CREATE_PVP_TEAMS_VIOLATION = 0x34, + CHAR_CREATE_SERVER_LIMIT = 0x35, + CHAR_CREATE_ACCOUNT_LIMIT = 0x36, + CHAR_CREATE_SERVER_QUEUE = 0x37, + CHAR_CREATE_ONLY_EXISTING = 0x38, + CHAR_CREATE_EXPANSION = 0x39, + + CHAR_DELETE_IN_PROGRESS = 0x3A, + CHAR_DELETE_SUCCESS = 0x3B, + CHAR_DELETE_FAILED = 0x3C, + CHAR_DELETE_FAILED_LOCKED_FOR_TRANSFER = 0x3D, + CHAR_DELETE_FAILED_GUILD_LEADER = 0x3E, + CHAR_DELETE_FAILED_ARENA_CAPTAIN = 0x3F, + + CHAR_LOGIN_IN_PROGRESS = 0x40, + CHAR_LOGIN_SUCCESS = 0x41, + CHAR_LOGIN_NO_WORLD = 0x42, + CHAR_LOGIN_DUPLICATE_CHARACTER = 0x43, + CHAR_LOGIN_NO_INSTANCES = 0x44, + CHAR_LOGIN_FAILED = 0x45, + CHAR_LOGIN_DISABLED = 0x46, + CHAR_LOGIN_NO_CHARACTER = 0x47, + CHAR_LOGIN_LOCKED_FOR_TRANSFER = 0x48, + CHAR_LOGIN_LOCKED_BY_BILLING = 0x49, + + CHAR_NAME_SUCCESS = 0x4A, + CHAR_NAME_FAILURE = 0x4B, + CHAR_NAME_NO_NAME = 0x4C, + CHAR_NAME_TOO_SHORT = 0x4D, + CHAR_NAME_TOO_LONG = 0x4E, + CHAR_NAME_INVALID_CHARACTER = 0x4F, + CHAR_NAME_MIXED_LANGUAGES = 0x50, + CHAR_NAME_PROFANE = 0x51, + CHAR_NAME_RESERVED = 0x52, + CHAR_NAME_INVALID_APOSTROPHE = 0x53, + CHAR_NAME_MULTIPLE_APOSTROPHES = 0x54, + CHAR_NAME_THREE_CONSECUTIVE = 0x55, + CHAR_NAME_INVALID_SPACE = 0x56, + CHAR_NAME_CONSECUTIVE_SPACES = 0x57, + CHAR_NAME_RUSSIAN_CONSECUTIVE_SILENT_CHARACTERS = 0x58, + CHAR_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END = 0x59, + CHAR_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 0x5A, +}; + +class WorldPacket; +class SocketHandler; +class WorldSession; + +/// Handle connection with the client software +class WorldSocket : public TcpSocket +{ + public: + WorldSocket(ISocketHandler&); + ~WorldSocket(); + + void SendPacket(WorldPacket const* packet); + void CloseSocket(); + + void OnAccept(); + void OnRead(); + void OnDelete(); + + void Update(time_t diff); + // Player Queue + void SendAuthWaitQue(uint32 position); + + WorldSession* GetSession() const { return _session; } + protected: + void SendSinglePacket(); + + protected: + void _HandleAuthSession(WorldPacket& recvPacket); + void _HandlePing(WorldPacket& recvPacket); + + private: + AuthCrypt _crypt; + uint32 _seed; + uint32 _cmd; + uint16 _remaining; + WorldSession* _session; + + ZThread::LockedQueue _sendQueue; + + uint32 m_LastPingMSTime; + uint32 m_OverSpeedPings; + + // internal checks + void SizeError(WorldPacket const& packet, uint32 size) const; +}; +#endif +/// @} diff --git a/src/game/WorldSocketMgr.cpp b/src/game/WorldSocketMgr.cpp new file mode 100644 index 00000000000..0281d200109 --- /dev/null +++ b/src/game/WorldSocketMgr.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2005-2008,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** \file + \ingroup u2w +*/ + +#include "Common.h" +#include "WorldSocket.h" +#include "WorldSocketMgr.h" +#include "Policies/SingletonImp.h" + +INSTANTIATE_SINGLETON_1( WorldSocketMgr ); + +/// WorldSocketMgr constructor +WorldSocketMgr::WorldSocketMgr() +{ +} + +/// Add a WorldSocket to the set +void WorldSocketMgr::AddSocket(WorldSocket *s) +{ + m_sockets.insert(s); +} + +/// Remove a WorldSocket to the set +void WorldSocketMgr::RemoveSocket(WorldSocket *s) +{ + m_sockets.erase(s); +} + +/// Triggers an 'update' to all sockets in the set +void WorldSocketMgr::Update(time_t diff) +{ + SocketSet::iterator i; + for(i = m_sockets.begin(); i != m_sockets.end(); i++) + { + (*i)->Update(diff); + } +} diff --git a/src/game/WorldSocketMgr.h b/src/game/WorldSocketMgr.h new file mode 100644 index 00000000000..5a26d739f4a --- /dev/null +++ b/src/game/WorldSocketMgr.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2008,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/// \addtogroup u2w User to World Communication +/// @{ +/// \file + +#ifndef __WORLDSOCKETMGR_H +#define __WORLDSOCKETMGR_H + +#include "Policies/Singleton.h" + +class WorldSocket; + +/// Manages the list of connected WorldSockets +class WorldSocketMgr +{ + public: + WorldSocketMgr(); + + void AddSocket(WorldSocket *s); + void RemoveSocket(WorldSocket *s); + void Update(time_t diff); + + private: + typedef std::set SocketSet; + SocketSet m_sockets; +}; + +#define sWorldSocketMgr MaNGOS::Singleton::Instance() +#endif +/// @} diff --git a/src/game/debugcmds.cpp b/src/game/debugcmds.cpp new file mode 100644 index 00000000000..98d0984f545 --- /dev/null +++ b/src/game/debugcmds.cpp @@ -0,0 +1,514 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" +#include "Database/DatabaseEnv.h" +#include "WorldPacket.h" +#include "WorldSession.h" +#include "World.h" +#include "Player.h" +#include "Opcodes.h" +#include "Chat.h" +#include "Log.h" +#include "Unit.h" +#include "ObjectAccessor.h" +#include "GossipDef.h" +#include "Language.h" +#include "MapManager.h" + +bool ChatHandler::HandleDebugInArcCommand(const char* /*args*/) +{ + Object *obj = getSelectedUnit(); + + if(!obj) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + return true; + } + + SendSysMessage(LANG_NOT_IMPLEMENTED); + + return true; +} + +bool ChatHandler::HandleDebugSpellFailCommand(const char* args) +{ + if(!args) + return false; + + char* px = strtok((char*)args, " "); + if(!px) + return false; + + uint8 failnum = (uint8)atoi(px); + + WorldPacket data(SMSG_CAST_FAILED, 5); + data << (uint32)133; + data << failnum; + m_session->SendPacket(&data); + + return true; +} + +bool ChatHandler::HandleSetPoiCommand(const char* args) +{ + Player *pPlayer = m_session->GetPlayer(); + Unit* target = getSelectedUnit(); + if(!target) + { + SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); + return true; + } + + if(!args) + return false; + + char* icon_text = strtok((char*)args, " "); + char* flags_text = strtok(NULL, " "); + if(!icon_text || !flags_text) + return false; + + uint32 icon = atol(icon_text); + if ( icon < 0 ) + icon = 0; + + uint32 flags = atol(flags_text); + + sLog.outDetail("Command : POI, NPC = %u, icon = %u flags = %u", target->GetGUIDLow(), icon,flags); + pPlayer->PlayerTalkClass->SendPointOfInterest(target->GetPositionX(), target->GetPositionY(), Poi_Icon(icon), flags, 30, "Test POI"); + return true; +} + +bool ChatHandler::HandleEquipErrorCommand(const char* args) +{ + if(!args) + return false; + + uint8 msg = atoi(args); + m_session->GetPlayer()->SendEquipError(msg, 0, 0); + return true; +} + +bool ChatHandler::HandleSellErrorCommand(const char* args) +{ + if(!args) + return false; + + uint8 msg = atoi(args); + m_session->GetPlayer()->SendSellError(msg, 0, 0, 0); + return true; +} + +bool ChatHandler::HandleBuyErrorCommand(const char* args) +{ + if(!args) + return false; + + uint8 msg = atoi(args); + m_session->GetPlayer()->SendBuyError(msg, 0, 0, 0); + return true; +} + +bool ChatHandler::HandleSendOpcodeCommand(const char* args) +{ + Unit *unit = getSelectedUnit(); + if (!unit || (unit->GetTypeId() != TYPEID_PLAYER)) + unit = m_session->GetPlayer(); + + FILE *file = fopen("opcode.txt", "r"); + if(!file) + return false; + + uint32 type; + + uint32 val1; + uint64 val2; + float val3; + char val4[101]; + + uint32 opcode = 0; + fscanf(file, "%u", &opcode); + if(!opcode) + { + fclose(file); + return false; + } + + WorldPacket data(opcode, 0); + + while(fscanf(file, "%u", &type) != EOF) + { + switch(type) + { + case 0: // uint8 + fscanf(file, "%u", &val1); + data << uint8(val1); + break; + case 1: // uint16 + fscanf(file, "%u", &val1); + data << uint16(val1); + break; + case 2: // uint32 + fscanf(file, "%u", &val1); + data << uint32(val1); + break; + case 3: // uint64 + fscanf(file, I64FMTD, &val2); + data << uint64(val2); + break; + case 4: // float + fscanf(file, "%f", &val3); + data << float(val3); + break; + case 5: // string + fscanf(file, "%s", val4, 101); + data << val4; + break; + case 6: // packed guid + data.append(unit->GetPackGUID()); + break; + default: + fclose(file); + return false; + } + } + fclose(file); + sLog.outDebug("Sending opcode %u", data.GetOpcode()); + data.hexlike(); + ((Player*)unit)->GetSession()->SendPacket(&data); + PSendSysMessage(LANG_COMMAND_OPCODESENT, data.GetOpcode(), unit->GetName()); + return true; +} + +bool ChatHandler::HandleUpdateWorldStateCommand(const char* args) +{ + char* w = strtok((char*)args, " "); + char* s = strtok(NULL, " "); + + if (!w || !s) + return false; + + uint32 world = (uint32)atoi(w); + uint32 state = (uint32)atoi(s); + m_session->GetPlayer()->SendUpdateWorldState(world, state); + return true; +} + +bool ChatHandler::HandlePlaySound2Command(const char* args) +{ + if(!args) + return false; + + uint32 soundid = atoi(args); + m_session->GetPlayer()->PlaySound(soundid, false); + return true; +} + +//Send notification in channel +bool ChatHandler::HandleSendChannelNotifyCommand(const char* args) +{ + if(!args) + return false; + + const char *name = "test"; + uint8 code = atoi(args); + + WorldPacket data(SMSG_CHANNEL_NOTIFY, (1+10)); + data << code; // notify type + data << name; // channel name + data << uint32(0); + data << uint32(0); + m_session->SendPacket(&data); + return true; +} + +//Send notification in chat +bool ChatHandler::HandleSendChatMsgCommand(const char* args) +{ + if(!args) + return false; + + const char *msg = "testtest"; + uint8 type = atoi(args); + WorldPacket data; + ChatHandler::FillMessageData(&data, m_session, type, 0, "chan", m_session->GetPlayer()->GetGUID(), msg, m_session->GetPlayer()); + m_session->SendPacket(&data); + return true; +} + +bool ChatHandler::HandleSendQuestPartyMsgCommand(const char* args) +{ + uint32 msg = atol((char*)args); + if (msg >= 0) + m_session->GetPlayer()->SendPushToPartyResponse(m_session->GetPlayer(), msg); + return true; +} + +bool ChatHandler::HandleGetLootRecipient(const char* args) +{ + Creature* target = getSelectedCreature(); + if(!target) + return false; + + PSendSysMessage("loot recipient: %s", target->hasLootRecipient()?(target->GetLootRecipient()?target->GetLootRecipient()->GetName():"offline"):"no loot recipient"); + return true; +} + +bool ChatHandler::HandleSendQuestInvalidMsgCommand(const char* args) +{ + uint32 msg = atol((char*)args); + if (msg >= 0) + m_session->GetPlayer()->SendCanTakeQuestResponse(msg); + return true; +} + +bool ChatHandler::HandleGetItemState(const char* args) +{ + if (!args) + return false; + + std::string state_str = args; + + ItemUpdateState state = ITEM_UNCHANGED; + bool list_queue = false, check_all = false; + if (state_str == "unchanged") state = ITEM_UNCHANGED; + else if (state_str == "changed") state = ITEM_CHANGED; + else if (state_str == "new") state = ITEM_NEW; + else if (state_str == "removed") state = ITEM_REMOVED; + else if (state_str == "queue") list_queue = true; + else if (state_str == "check_all") check_all = true; + else return false; + + Player* player = getSelectedPlayer(); + if (!player) player = m_session->GetPlayer(); + + if (!list_queue && !check_all) + { + state_str = "The player has the following " + state_str + " items: "; + SendSysMessage(state_str.c_str()); + for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++) + { + if(i >= BUYBACK_SLOT_START && i < BUYBACK_SLOT_END) + continue; + + Item *item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (!item) continue; + if (!item->IsBag()) + { + if (item->GetState() == state) + PSendSysMessage("bag: 255 slot: %d guid: %d owner: %d", item->GetSlot(), item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID())); + } + else + { + Bag *bag = (Bag*)item; + const ItemPrototype *proto = bag->GetProto(); + for (uint8 j = 0; j < proto->ContainerSlots; ++j) + { + Item* item = bag->GetItemByPos(j); + if (item && item->GetState() == state) + PSendSysMessage("bag: 255 slot: %d guid: %d owner: %d", item->GetSlot(), item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID())); + } + } + } + } + + if (list_queue) + { + std::vector &updateQueue = player->GetItemUpdateQueue(); + for(size_t i = 0; i < updateQueue.size(); i++) + { + Item *item = updateQueue[i]; + if(!item) continue; + + Bag *container = item->GetContainer(); + uint8 bag_slot = container ? container->GetSlot() : uint8(INVENTORY_SLOT_BAG_0); + + std::string st; + switch(item->GetState()) + { + case ITEM_UNCHANGED: st = "unchanged"; break; + case ITEM_CHANGED: st = "changed"; break; + case ITEM_NEW: st = "new"; break; + case ITEM_REMOVED: st = "removed"; break; + } + + PSendSysMessage("bag: %d slot: %d guid: %d - state: %s", bag_slot, item->GetSlot(), item->GetGUIDLow(), st.c_str()); + } + if (updateQueue.empty()) + PSendSysMessage("updatequeue empty"); + } + + if (check_all) + { + bool error = false; + std::vector &updateQueue = player->GetItemUpdateQueue(); + for (uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; i++) + { + if(i >= BUYBACK_SLOT_START && i < BUYBACK_SLOT_END) + continue; + + Item *item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (!item) continue; + + if (item->GetSlot() != i) + { + PSendSysMessage("item at slot %d, guid %d has an incorrect slot value: %d", i, item->GetGUIDLow(), item->GetSlot()); + error = true; continue; + } + + if (item->GetOwnerGUID() != player->GetGUID()) + { + PSendSysMessage("for the item at slot %d and itemguid %d, owner's guid (%d) and player's guid (%d) don't match!", item->GetSlot(), item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()), player->GetGUIDLow()); + error = true; continue; + } + + if (Bag *container = item->GetContainer()) + { + PSendSysMessage("item at slot: %d guid: %d has a container (slot: %d, guid: %d) but shouldnt!", item->GetSlot(), item->GetGUIDLow(), container->GetSlot(), container->GetGUIDLow()); + error = true; continue; + } + + if (item->IsInUpdateQueue()) + { + uint16 qp = item->GetQueuePos(); + if (qp > updateQueue.size()) + { + PSendSysMessage("item at slot: %d guid: %d has a queuepos (%d) larger than the update queue size! ", item->GetSlot(), item->GetGUIDLow(), qp); + error = true; continue; + } + + if (updateQueue[qp] == NULL) + { + PSendSysMessage("item at slot: %d guid: %d has a queuepos (%d) that points to NULL in the queue!", item->GetSlot(), item->GetGUIDLow(), qp); + error = true; continue; + } + + if (updateQueue[qp] != item) + { + PSendSysMessage("item at slot: %d guid: %d has has a queuepos (%d) that points to another item in the queue (bag: %d, slot: %d, guid: %d)", item->GetSlot(), item->GetGUIDLow(), qp, updateQueue[qp]->GetBagSlot(), updateQueue[qp]->GetSlot(), updateQueue[qp]->GetGUIDLow()); + error = true; continue; + } + } + else if (item->GetState() != ITEM_UNCHANGED) + { + PSendSysMessage("item at slot: %d guid: %d is not in queue but should be (state: %d)!", item->GetSlot(), item->GetGUIDLow(), item->GetState()); + error = true; continue; + } + + if(item->IsBag()) + { + Bag *bag = (Bag*)item; + const ItemPrototype *proto = bag->GetProto(); + for (uint8 j = 0; j < proto->ContainerSlots; ++j) + { + Item* item = bag->GetItemByPos(j); + if (!item) continue; + + if (item->GetSlot() != j) + { + PSendSysMessage("the item in bag %d slot %d, guid %d has an incorrect slot value: %d", bag->GetSlot(), j, item->GetGUIDLow(), item->GetSlot()); + error = true; continue; + } + + if (item->GetOwnerGUID() != player->GetGUID()) + { + PSendSysMessage("for the item in bag %d at slot %d and itemguid %d, owner's guid (%d) and player's guid (%d) don't match!", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()), player->GetGUIDLow()); + error = true; continue; + } + + Bag *container = item->GetContainer(); + if (!container) + { + PSendSysMessage("the item in bag %d at slot %d with guid %d has no container!", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow()); + error = true; continue; + } + + if (container != bag) + { + PSendSysMessage("the item in bag %d at slot %d with guid %d has a different container(slot %d guid %d)!", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), container->GetSlot(), container->GetGUIDLow()); + error = true; continue; + } + + if (item->IsInUpdateQueue()) + { + uint16 qp = item->GetQueuePos(); + if (qp > updateQueue.size()) + { + PSendSysMessage("item in bag: %d at slot: %d guid: %d has a queuepos (%d) larger than the update queue size! ", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), qp); + error = true; continue; + } + + if (updateQueue[qp] == NULL) + { + PSendSysMessage("item in bag: %d at slot: %d guid: %d has a queuepos (%d) that points to NULL in the queue!", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), qp); + error = true; continue; + } + + if (updateQueue[qp] != item) + { + PSendSysMessage("item in bag: %d at slot: %d guid: %d has has a queuepos (%d) that points to another item in the queue (bag: %d, slot: %d, guid: %d)", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), qp, updateQueue[qp]->GetBagSlot(), updateQueue[qp]->GetSlot(), updateQueue[qp]->GetGUIDLow()); + error = true; continue; + } + } + else if (item->GetState() != ITEM_UNCHANGED) + { + PSendSysMessage("item in bag: %d at slot: %d guid: %d is not in queue but should be (state: %d)!", bag->GetSlot(), item->GetSlot(), item->GetGUIDLow(), item->GetState()); + error = true; continue; + } + } + } + } + + for(size_t i = 0; i < updateQueue.size(); i++) + { + Item *item = updateQueue[i]; + if(!item) continue; + + if (item->GetOwnerGUID() != player->GetGUID()) + { + PSendSysMessage("queue(%d): for the an item (guid %d), the owner's guid (%d) and player's guid (%d) don't match!", i, item->GetGUIDLow(), GUID_LOPART(item->GetOwnerGUID()), player->GetGUIDLow()); + error = true; continue; + } + + if (item->GetQueuePos() != i) + { + PSendSysMessage("queue(%d): for the an item (guid %d), the queuepos doesn't match it's position in the queue!", i, item->GetGUIDLow()); + error = true; continue; + } + + if (item->GetState() == ITEM_REMOVED) continue; + Item *test = player->GetItemByPos( item->GetBagSlot(), item->GetSlot()); + + if (test == NULL) + { + PSendSysMessage("queue(%d): 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!", i, item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow()); + error = true; continue; + } + + if (test != item) + { + PSendSysMessage("queue(%d): the bag(%d) and slot(%d) values for the item with guid %d are incorrect, the item with guid %d is there instead!", i, item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), test->GetGUIDLow()); + error = true; continue; + } + } + if (!error) + SendSysMessage("All OK!"); + } + + return true; +} diff --git a/src/game/tools.cpp b/src/game/tools.cpp new file mode 100644 index 00000000000..7b4b4cffbaa --- /dev/null +++ b/src/game/tools.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Tools.h" + +// THIS CAN BE A LOT FASTER +bool readGUID(WorldPacket & data, uint64& guid) +{ + if(data.rpos()+1 > data.size()) + return false; + + uint8 guidmark=0; + uint8 bit; + uint8 shiftdata=0x1; + uint64 Temp=0; + + guid = 0; + + data >> guidmark; + for(int i=0;i<8;i++) + { + if(guidmark & shiftdata) + { + Temp = 0; + + if(data.rpos()+1 > data.size()) + return false; + + data >> bit; + Temp = bit; + Temp <<= i*8; + guid |= Temp; + } + shiftdata=shiftdata<<1; + } + + return true; +} + +void writeGUID(WorldPacket & data, uint64 & guid) +{ + uint8 RAWmask = 0; + uint8 PackedGuid[8] = {0,0,0,0,0,0,0,0}; + + int j = 1; + uint8 * test = (uint8*)&guid; + + if (*test) + { + PackedGuid[j] = *test; + RAWmask |= 1; + ++j; + } + if (*(test+1)) + { + PackedGuid[j] = *(test+1); + RAWmask |= 2; + ++j; + } + if (*(test+2)) + { + PackedGuid[j] = *(test+2); + RAWmask |= 4; + ++j; + } + if (*(test+3)) + { + PackedGuid[j] = *(test+3); + RAWmask |= 8; + ++j; + } + if (*(test+4)) + { + PackedGuid[j] = *(test+4); + RAWmask |= 16; + ++j; + } + if (*(test+5)) + { + PackedGuid[j] = *(test+5); + RAWmask |= 32; + ++j; + } + if (*(test+6)) + { + PackedGuid[j] = *(test+6); + RAWmask |= 64; + ++j; + } + if (*(test+7)) + { + PackedGuid[j] = *(test+7); + RAWmask |= 128; + ++j; + } + PackedGuid[0] = RAWmask; + + data.append(PackedGuid,j); +} diff --git a/src/shared/Auth/AuthCrypt.cpp b/src/shared/Auth/AuthCrypt.cpp new file mode 100644 index 00000000000..56143c3417c --- /dev/null +++ b/src/shared/Auth/AuthCrypt.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "AuthCrypt.h" +#include "Hmac.h" + +AuthCrypt::AuthCrypt() +{ + _initialized = false; +} + +void AuthCrypt::Init() +{ + _send_i = _send_j = _recv_i = _recv_j = 0; + _initialized = true; +} + +void AuthCrypt::DecryptRecv(uint8 *data, size_t len) +{ + if (!_initialized) return; + if (len < CRYPTED_RECV_LEN) return; + + for (size_t t = 0; t < CRYPTED_RECV_LEN; t++) + { + _recv_i %= _key.size(); + uint8 x = (data[t] - _recv_j) ^ _key[_recv_i]; + ++_recv_i; + _recv_j = data[t]; + data[t] = x; + } +} + +void AuthCrypt::EncryptSend(uint8 *data, size_t len) +{ + if (!_initialized) return; + if (len < CRYPTED_SEND_LEN) return; + + for (size_t t = 0; t < CRYPTED_SEND_LEN; t++) + { + _send_i %= _key.size(); + uint8 x = (data[t] ^ _key[_send_i]) + _send_j; + ++_send_i; + data[t] = _send_j = x; + } +} + +void AuthCrypt::SetKey(BigNumber *bn) +{ + uint8 *key = new uint8[SHA_DIGEST_LENGTH]; + GenerateKey(key, bn); + _key.resize(SHA_DIGEST_LENGTH); + std::copy(key, key + SHA_DIGEST_LENGTH, _key.begin()); + delete key; +} + +AuthCrypt::~AuthCrypt() +{ +} + +void AuthCrypt::GenerateKey(uint8 *key, BigNumber *bn) +{ + HmacHash hash; + hash.UpdateBigNumber(bn); + hash.Finalize(); + memcpy(key, hash.GetDigest(), SHA_DIGEST_LENGTH); +} diff --git a/src/shared/Auth/AuthCrypt.h b/src/shared/Auth/AuthCrypt.h new file mode 100644 index 00000000000..94e075e584e --- /dev/null +++ b/src/shared/Auth/AuthCrypt.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AUTHCRYPT_H +#define _AUTHCRYPT_H + +#include +#include + +class BigNumber; + +class AuthCrypt +{ + public: + AuthCrypt(); + ~AuthCrypt(); + + const static size_t CRYPTED_SEND_LEN = 4; + const static size_t CRYPTED_RECV_LEN = 6; + + void Init(); + + void SetKey(BigNumber *); + + void DecryptRecv(uint8 *, size_t); + void EncryptSend(uint8 *, size_t); + + bool IsInitialized() { return _initialized; } + + static void GenerateKey(uint8 *, BigNumber *); + + private: + std::vector _key; + uint8 _send_i, _send_j, _recv_i, _recv_j; + bool _initialized; +}; +#endif diff --git a/src/shared/Auth/BigNumber.cpp b/src/shared/Auth/BigNumber.cpp new file mode 100644 index 00000000000..752e82f171e --- /dev/null +++ b/src/shared/Auth/BigNumber.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Auth/BigNumber.h" +#include +#include + +BigNumber::BigNumber() +{ + _bn = BN_new(); + _array = NULL; +} + +BigNumber::BigNumber(const BigNumber &bn) +{ + _bn = BN_dup(bn._bn); + _array = NULL; +} + +BigNumber::BigNumber(uint32 val) +{ + _bn = BN_new(); + BN_set_word(_bn, val); + _array = NULL; +} + +BigNumber::~BigNumber() +{ + BN_free(_bn); + if(_array) delete[] _array; +} + +void BigNumber::SetDword(uint32 val) +{ + BN_set_word(_bn, val); +} + +void BigNumber::SetQword(uint64 val) +{ + BN_add_word(_bn, (uint32)(val >> 32)); + BN_lshift(_bn, _bn, 32); + BN_add_word(_bn, (uint32)(val & 0xFFFFFFFF)); +} + +void BigNumber::SetBinary(const uint8 *bytes, int len) +{ + uint8 t[1000]; + for (int i = 0; i < len; i++) t[i] = bytes[len - 1 - i]; + BN_bin2bn(t, len, _bn); +} + +void BigNumber::SetHexStr(const char *str) +{ + BN_hex2bn(&_bn, str); +} + +void BigNumber::SetRand(int numbits) +{ + BN_rand(_bn, numbits, 0, 1); +} + +BigNumber BigNumber::operator=(const BigNumber &bn) +{ + BN_copy(_bn, bn._bn); + return *this; +} + +BigNumber BigNumber::operator+=(const BigNumber &bn) +{ + BN_add(_bn, _bn, bn._bn); + return *this; +} + +BigNumber BigNumber::operator-=(const BigNumber &bn) +{ + BN_sub(_bn, _bn, bn._bn); + return *this; +} + +BigNumber BigNumber::operator*=(const BigNumber &bn) +{ + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + BN_mul(_bn, _bn, bn._bn, bnctx); + BN_CTX_free(bnctx); + + return *this; +} + +BigNumber BigNumber::operator/=(const BigNumber &bn) +{ + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + BN_div(_bn, NULL, _bn, bn._bn, bnctx); + BN_CTX_free(bnctx); + + return *this; +} + +BigNumber BigNumber::operator%=(const BigNumber &bn) +{ + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + BN_mod(_bn, _bn, bn._bn, bnctx); + BN_CTX_free(bnctx); + + return *this; +} + +BigNumber BigNumber::Exp(const BigNumber &bn) +{ + BigNumber ret; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + BN_exp(ret._bn, _bn, bn._bn, bnctx); + BN_CTX_free(bnctx); + + return ret; +} + +BigNumber BigNumber::ModExp(const BigNumber &bn1, const BigNumber &bn2) +{ + BigNumber ret; + BN_CTX *bnctx; + + bnctx = BN_CTX_new(); + BN_mod_exp(ret._bn, _bn, bn1._bn, bn2._bn, bnctx); + BN_CTX_free(bnctx); + + return ret; +} + +int BigNumber::GetNumBytes(void) +{ + return BN_num_bytes(_bn); +} + +uint32 BigNumber::AsDword() +{ + return (uint32)BN_get_word(_bn); +} + +uint8 *BigNumber::AsByteArray(int minSize) +{ + int length = (minSize >= GetNumBytes()) ? minSize : GetNumBytes(); + + if (_array) + { + delete[] _array; + _array = NULL; + } + _array = new uint8[length]; + + // If we need more bytes than length of BigNumber set the rest to 0 + if (length > GetNumBytes()) + memset((void*)_array, 0, length); + + BN_bn2bin(_bn, (unsigned char *)_array); + + std::reverse(_array, _array + length); + + return _array; +} + +ByteBuffer BigNumber::AsByteBuffer() +{ + ByteBuffer ret(GetNumBytes()); + ret.append(AsByteArray(), GetNumBytes()); + return ret; +} + +std::vector BigNumber::AsByteVector() +{ + std::vector ret; + ret.resize(GetNumBytes()); + memcpy(&ret[0], AsByteArray(), GetNumBytes()); + return ret; +} + +const char *BigNumber::AsHexStr() +{ + return BN_bn2hex(_bn); +} + +const char *BigNumber::AsDecStr() +{ + return BN_bn2dec(_bn); +} diff --git a/src/shared/Auth/BigNumber.h b/src/shared/Auth/BigNumber.h new file mode 100644 index 00000000000..c66798afd85 --- /dev/null +++ b/src/shared/Auth/BigNumber.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AUTH_BIGNUMBER_H +#define _AUTH_BIGNUMBER_H + +#include "Common.h" +#include "ByteBuffer.h" + +struct bignum_st; + +class BigNumber +{ + public: + BigNumber(); + BigNumber(const BigNumber &bn); + BigNumber(uint32); + ~BigNumber(); + + void SetDword(uint32); + void SetQword(uint64); + void SetBinary(const uint8 *bytes, int len); + void SetHexStr(const char *str); + + void SetRand(int numbits); + + BigNumber operator=(const BigNumber &bn); + + BigNumber operator+=(const BigNumber &bn); + BigNumber operator+(const BigNumber &bn) + { + BigNumber t(*this); + return t += bn; + } + BigNumber operator-=(const BigNumber &bn); + BigNumber operator-(const BigNumber &bn) + { + BigNumber t(*this); + return t -= bn; + } + BigNumber operator*=(const BigNumber &bn); + BigNumber operator*(const BigNumber &bn) + { + BigNumber t(*this); + return t *= bn; + } + BigNumber operator/=(const BigNumber &bn); + BigNumber operator/(const BigNumber &bn) + { + BigNumber t(*this); + return t /= bn; + } + BigNumber operator%=(const BigNumber &bn); + BigNumber operator%(const BigNumber &bn) + { + BigNumber t(*this); + return t %= bn; + } + + BigNumber ModExp(const BigNumber &bn1, const BigNumber &bn2); + BigNumber Exp(const BigNumber &); + + int GetNumBytes(void); + + struct bignum_st *BN() { return _bn; } + + uint32 AsDword(); + uint8* AsByteArray(int minSize = 0); + ByteBuffer AsByteBuffer(); + std::vector AsByteVector(); + + const char *AsHexStr(); + const char *AsDecStr(); + + private: + struct bignum_st *_bn; + uint8 *_array; +}; +#endif diff --git a/src/shared/Auth/Hmac.cpp b/src/shared/Auth/Hmac.cpp new file mode 100644 index 00000000000..d2054a0a777 --- /dev/null +++ b/src/shared/Auth/Hmac.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Auth/Hmac.h" +#include "BigNumber.h" + +HmacHash::HmacHash() +{ + uint8 temp[SEED_KEY_SIZE] = { 0x38, 0xA7, 0x83, 0x15, 0xF8, 0x92, 0x25, 0x30, 0x71, 0x98, 0x67, 0xB1, 0x8C, 0x4, 0xE2, 0xAA }; + memcpy(&m_key, &temp, SEED_KEY_SIZE); + HMAC_CTX_init(&m_ctx); + HMAC_Init_ex(&m_ctx, &m_key, SEED_KEY_SIZE, EVP_sha1(), NULL); +} + +HmacHash::~HmacHash() +{ + memset(&m_key, 0x00, SEED_KEY_SIZE); + HMAC_CTX_cleanup(&m_ctx); +} + +void HmacHash::UpdateBigNumber(BigNumber *bn) +{ + UpdateData(bn->AsByteArray(), bn->GetNumBytes()); +} + +void HmacHash::UpdateData(const uint8 *data, int length) +{ + HMAC_Update(&m_ctx, data, length); +} + +void HmacHash::Initialize() +{ + HMAC_Init_ex(&m_ctx, &m_key, SEED_KEY_SIZE, EVP_sha1(), NULL); +} + +void HmacHash::Finalize() +{ + uint32 length = 0; + HMAC_Final(&m_ctx, m_digest, &length); + ASSERT(length == SHA_DIGEST_LENGTH) +} diff --git a/src/shared/Auth/Hmac.h b/src/shared/Auth/Hmac.h new file mode 100644 index 00000000000..083c84fe871 --- /dev/null +++ b/src/shared/Auth/Hmac.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AUTH_HMAC_H +#define _AUTH_HMAC_H + +#include "Common.h" +#include +#include + +class BigNumber; + +#define SEED_KEY_SIZE 16 + +class HmacHash +{ + public: + HmacHash(); + ~HmacHash(); + void UpdateBigNumber(BigNumber *bn); + void UpdateData(const uint8 *data, int length); + void Initialize(); + void Finalize(); + uint8 *GetDigest() { return m_digest; }; + int GetLength() { return SHA_DIGEST_LENGTH; }; + private: + HMAC_CTX m_ctx; + uint8 m_key[SEED_KEY_SIZE]; + uint8 m_digest[SHA_DIGEST_LENGTH]; +}; +#endif diff --git a/src/shared/Auth/Makefile.am b/src/shared/Auth/Makefile.am new file mode 100644 index 00000000000..6646568ee35 --- /dev/null +++ b/src/shared/Auth/Makefile.am @@ -0,0 +1,39 @@ +# Copyright (C) 2005-2008 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## CPP flags for includes, defines, etc. +AM_CPPFLAGS = $(MYSQL_INCLUDES) $(POSTGRE_INCLUDES) -I$(top_builddir)/src/shared -I$(srcdir) -I$(srcdir)/../../../dep/include -I$(srcdir)/../../framework -I$(srcdir)/../../shared -I$(srcdir)/../../../dep/include/g3dlite + +## Build MaNGOS shared library and its parts as convenience library. +# All libraries will be convenience libraries. Might be changed to shared +# later. +noinst_LIBRARIES = libmangosauth.a + +libmangosauth_a_SOURCES = \ + AuthCrypt.cpp \ + AuthCrypt.h \ + BigNumber.cpp \ + BigNumber.h \ + Hmac.cpp \ + Hmac.h \ + Sha1.cpp \ + Sha1.h \ + md5.c \ + md5.h diff --git a/src/shared/Auth/Sha1.cpp b/src/shared/Auth/Sha1.cpp new file mode 100644 index 00000000000..6a4a3967b26 --- /dev/null +++ b/src/shared/Auth/Sha1.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Auth/Sha1.h" +#include + +Sha1Hash::Sha1Hash() +{ + SHA1_Init(&mC); +} + +Sha1Hash::~Sha1Hash() +{ + SHA1_Init(&mC); +} + +void Sha1Hash::UpdateData(const uint8 *dta, int len) +{ + SHA1_Update(&mC, dta, len); +} + +void Sha1Hash::UpdateData(const std::string &str) +{ + UpdateData((uint8 const*)str.c_str(), str.length()); +} + +void Sha1Hash::UpdateBigNumbers(BigNumber *bn0, ...) +{ + va_list v; + BigNumber *bn; + + va_start(v, bn0); + bn = bn0; + while (bn) + { + UpdateData(bn->AsByteArray(), bn->GetNumBytes()); + bn = va_arg(v, BigNumber *); + } + va_end(v); +} + +void Sha1Hash::Initialize() +{ + SHA1_Init(&mC); +} + +void Sha1Hash::Finalize(void) +{ + SHA1_Final(mDigest, &mC); +} diff --git a/src/shared/Auth/Sha1.h b/src/shared/Auth/Sha1.h new file mode 100644 index 00000000000..3be4bcb8159 --- /dev/null +++ b/src/shared/Auth/Sha1.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AUTH_SHA1_H +#define _AUTH_SHA1_H + +#include "Common.h" +#include +#include +#include "Auth/BigNumber.h" + +class Sha1Hash +{ + public: + Sha1Hash(); + ~Sha1Hash(); + + void UpdateFinalizeBigNumbers(BigNumber *bn0, ...); + void UpdateBigNumbers(BigNumber *bn0, ...); + + void UpdateData(const uint8 *dta, int len); + void UpdateData(const std::string &str); + + void Initialize(); + void Finalize(); + + uint8 *GetDigest(void) { return mDigest; }; + int GetLength(void) { return SHA_DIGEST_LENGTH; }; + + BigNumber GetBigNumber(); + + private: + SHA_CTX mC; + uint8 mDigest[SHA_DIGEST_LENGTH]; +}; +#endif diff --git a/src/shared/Auth/md5.c b/src/shared/Auth/md5.c new file mode 100644 index 00000000000..3e9735e2dbb --- /dev/null +++ b/src/shared/Auth/md5.c @@ -0,0 +1,385 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +L. Peter Deutsch +ghost@aladdin.com + +*/ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + +The original and principal author of md5.c is L. Peter Deutsch +. Other authors are noted in the change history +that follows (in reverse chronological order): + +2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order +either statically or dynamically; added missing #include +in library. +2002-03-11 lpd Corrected argument list for main(), and added int return +type, in test program and T value program. +2002-02-21 lpd Added missing #include in test program. +2000-07-03 lpd Patched to eliminate warnings about "constant is +unsigned in ANSI C, signed in traditional"; made test program +self-checking. +1999-11-04 lpd Edited comments slightly for automatic TOC extraction. +1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). +1999-05-03 lpd Original version. +*/ + +#include "md5.h" +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; + #if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; + #else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; + #endif + + { + #if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ + #endif + #if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) + { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } + else + { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } + #endif + #if BYTE_ORDER == 0 + else /* dynamic big-endian */ + #endif + #if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + + # if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ + # else + # define xbuf X /* (static only) */ + # endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } + #endif + } + + #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ + #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) + #define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); + #undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ + #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) + #define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); + #undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ + #define H(x, y, z) ((x) ^ (y) ^ (z)) + #define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); + #undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ + #define I(x, y, z) ((y) ^ ((x) | ~(z))) + #define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); + #undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + ++pms->count[1]; + + /* Process an initial partial block. */ + if (offset) + { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = + { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/src/shared/Auth/md5.h b/src/shared/Auth/md5.h new file mode 100644 index 00000000000..fa2937e13e4 --- /dev/null +++ b/src/shared/Auth/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +L. Peter Deutsch +ghost@aladdin.com + +*/ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + +The original and principal author of md5.h is L. Peter Deutsch +. Other authors are noted in the change history +that follows (in reverse chronological order): + +2002-04-13 lpd Removed support for non-ANSI compilers; removed +references to Ghostscript; clarified derivation from RFC 1321; +now handles byte order either statically or dynamically. +1999-11-04 lpd Edited comments slightly for automatic TOC extraction. +1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); +added conditionalization for C++ compilation from Martin +Purschke . +1999-05-03 lpd Original version. +*/ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s +{ + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ + #endif + + /* Initialize the algorithm. */ + void md5_init(md5_state_t *pms); + + /* Append a string to the message. */ + void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + + /* Finish the message and return the digest. */ + void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + + #ifdef __cplusplus +} /* end extern "C" */ +#endif +#endif /* md5_INCLUDED */ diff --git a/src/shared/Base.cpp b/src/shared/Base.cpp new file mode 100644 index 00000000000..9929cd41fdf --- /dev/null +++ b/src/shared/Base.cpp @@ -0,0 +1,67 @@ +/* + Base class interface + Copyright (C) 1998,1999 by Andrew Zabolotny + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "Base.h" + +Base::~Base () +{ +} + +/** + * Decrement object's reference count; as soon as the last reference + * to the object is removed, it is destroyed. + */ + +void Base::DecRef () +{ + if (!--RefCount) + delete this; +} + +/** + * Object initialization. The initial reference count is set to one; + * this means if you call DecRef() immediately after creating the object, + * it will be destroyed. + */ +Base::Base () +{ + RefCount = 1; +} + +/** + * Increment reference count. + * Every time when you copy a pointer to a object and store it for + * later use you MUST call IncRef() on it; this will allow to keep + * objects as long as they are referenced by some entity. + */ +void Base::IncRef () +{ + ++RefCount; + +} + +/** + * Query number of references to this object. + * I would rather prefer to have the reference counter strictly private, + * but sometimes, mostly for debugging, such a function can help. + */ +int Base::GetRefCount () +{ + return RefCount; +} diff --git a/src/shared/Base.h b/src/shared/Base.h new file mode 100644 index 00000000000..d5907fdf473 --- /dev/null +++ b/src/shared/Base.h @@ -0,0 +1,54 @@ +/* + Base class interface + Copyright (C) 1998,1999 by Andrew Zabolotny + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __BASE_H__ +#define __BASE_H__ + +#include "Common.h" + +/** + * This class is intended to be a base class for every other class. + * It defines the basic interface available for any object. + */ +class Base +{ + private: + /// Object reference count + int RefCount; + + protected: + /** + * Destroy this object. Destructor is virtual, because class contains + * virtual methods; also it is private because it is never intended + * to be called directly; use DecRef() instead: when reference counter + * reaches zero, the object will be destroyed. + */ + virtual ~Base (); + + public: + + Base (); + + void IncRef (); + + void DecRef (); + int GetRefCount (); + +}; +#endif // __BASE_H__ diff --git a/src/shared/ByteBuffer.h b/src/shared/ByteBuffer.h new file mode 100644 index 00000000000..d89466c2b1f --- /dev/null +++ b/src/shared/ByteBuffer.h @@ -0,0 +1,479 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _BYTEBUFFER_H +#define _BYTEBUFFER_H + +#include "Common.h" +#include "Errors.h" +#include "Log.h" +#include "Utilities/ByteConverter.h" + +class ByteBuffer +{ + public: + const static size_t DEFAULT_SIZE = 0x1000; + + // constructor + ByteBuffer(): _rpos(0), _wpos(0) + { + _storage.reserve(DEFAULT_SIZE); + } + // constructor + ByteBuffer(size_t res): _rpos(0), _wpos(0) + { + _storage.reserve(res); + } + // copy constructor + ByteBuffer(const ByteBuffer &buf): _rpos(buf._rpos), _wpos(buf._wpos), _storage(buf._storage) { } + + void clear() + { + _storage.clear(); + _rpos = _wpos = 0; + } + + template void append(T value) + { + EndianConvert(value); + append((uint8 *)&value, sizeof(value)); + } + + template void put(size_t pos,T value) + { + EndianConvert(value); + put(pos,(uint8 *)&value,sizeof(value)); + } + + ByteBuffer &operator<<(uint8 value) + { + append(value); + return *this; + } + ByteBuffer &operator<<(uint16 value) + { + append(value); + return *this; + } + ByteBuffer &operator<<(uint32 value) + { + append(value); + return *this; + } + ByteBuffer &operator<<(uint64 value) + { + append(value); + return *this; + } + + // signed as in 2e complement + ByteBuffer &operator<<(int8 value) + { + append(value); + return *this; + } + ByteBuffer &operator<<(int16 value) + { + append(value); + return *this; + } + ByteBuffer &operator<<(int32 value) + { + append(value); + return *this; + } + ByteBuffer &operator<<(int64 value) + { + append(value); + return *this; + } + + // floating points + ByteBuffer &operator<<(float value) + { + append(value); + return *this; + } + ByteBuffer &operator<<(double value) + { + append(value); + return *this; + } + ByteBuffer &operator<<(const std::string &value) + { + append((uint8 const *)value.c_str(), value.length()); + append((uint8)0); + return *this; + } + ByteBuffer &operator<<(const char *str) + { + append((uint8 const *)str, str ? strlen(str) : 0); + append((uint8)0); + return *this; + } + + ByteBuffer &operator>>(bool &value) + { + value = read() > 0 ? true : false; + return *this; + } + + ByteBuffer &operator>>(uint8 &value) + { + value = read(); + return *this; + } + ByteBuffer &operator>>(uint16 &value) + { + value = read(); + return *this; + } + ByteBuffer &operator>>(uint32 &value) + { + value = read(); + return *this; + } + ByteBuffer &operator>>(uint64 &value) + { + value = read(); + return *this; + } + + //signed as in 2e complement + ByteBuffer &operator>>(int8 &value) + { + value = read(); + return *this; + } + ByteBuffer &operator>>(int16 &value) + { + value = read(); + return *this; + } + ByteBuffer &operator>>(int32 &value) + { + value = read(); + return *this; + } + ByteBuffer &operator>>(int64 &value) + { + value = read(); + return *this; + } + + ByteBuffer &operator>>(float &value) + { + value = read(); + return *this; + } + ByteBuffer &operator>>(double &value) + { + value = read(); + return *this; + } + ByteBuffer &operator>>(std::string& value) + { + value.clear(); + while (rpos() < size()) // prevent crash at wrong string format in packet + { + char c=read(); + if (c==0) + break; + value+=c; + } + return *this; + } + + uint8 operator[](size_t pos) + { + return read(pos); + } + + size_t rpos() const { return _rpos; } + + size_t rpos(size_t rpos_) + { + _rpos = rpos_; + return _rpos; + }; + + size_t wpos() const { return _wpos; } + + size_t wpos(size_t wpos_) + { + _wpos = wpos_; + return _wpos; + } + + template T read() + { + T r=read(_rpos); + _rpos += sizeof(T); + return r; + }; + template T read(size_t pos) const + { + ASSERT(pos + sizeof(T) <= size() || PrintPosError(false,pos,sizeof(T))); + T val = *((T const*)&_storage[pos]); + EndianConvert(val); + return val; + } + + void read(uint8 *dest, size_t len) + { + ASSERT(_rpos + len <= size() || PrintPosError(false,_rpos,len)); + memcpy(dest, &_storage[_rpos], len); + _rpos += len; + } + + const uint8 *contents() const { return &_storage[0]; } + + size_t size() const { return _storage.size(); } + bool empty() const { return _storage.empty(); } + + void resize(size_t newsize) + { + _storage.resize(newsize); + _rpos = 0; + _wpos = size(); + }; + void reserve(size_t ressize) + { + if (ressize > size()) _storage.reserve(ressize); + }; + + void append(const std::string& str) + { + append((uint8 const*)str.c_str(),str.size() + 1); + } + void append(const char *src, size_t cnt) + { + return append((const uint8 *)src, cnt); + } + template + void append(const T *src, size_t cnt) + { + return append((const uint8 *)src, cnt*sizeof(T)); + } + void append(const uint8 *src, size_t cnt) + { + if (!cnt) return; + + ASSERT(size() < 10000000); + + if (_storage.size() < _wpos + cnt) + _storage.resize(_wpos + cnt); + memcpy(&_storage[_wpos], src, cnt); + _wpos += cnt; + } + void append(const ByteBuffer& buffer) + { + if(buffer.size()) append(buffer.contents(),buffer.size()); + } + + void appendPackGUID(uint64 guid) + { + size_t mask_position = wpos(); + *this << uint8(0); + for(uint8 i = 0; i < 8; i++) + { + if(guid & 0xFF) + { + _storage[mask_position] |= uint8(1<>= 8; + } + } + + void put(size_t pos, const uint8 *src, size_t cnt) + { + ASSERT(pos + cnt <= size() || PrintPosError(true,pos,cnt)); + memcpy(&_storage[pos], src, cnt); + } + void print_storage() + { + if(!sLog.IsOutDebug()) // optimize disabled debug output + return; + + sLog.outDebug("STORAGE_SIZE: %u", size() ); + for(uint32 i = 0; i < size(); i++) + sLog.outDebugInLine("%u - ", read(i) ); + sLog.outDebug(" "); + } + + void textlike() + { + if(!sLog.IsOutDebug()) // optimize disabled debug output + return; + + sLog.outDebug("STORAGE_SIZE: %u", size() ); + for(uint32 i = 0; i < size(); i++) + sLog.outDebugInLine("%c", read(i) ); + sLog.outDebug(" "); + } + + void hexlike() + { + if(!sLog.IsOutDebug()) // optimize disabled debug output + return; + + uint32 j = 1, k = 1; + sLog.outDebug("STORAGE_SIZE: %u", size() ); + + if(sLog.IsIncludeTime()) + sLog.outDebugInLine(" "); + + for(uint32 i = 0; i < size(); i++) + { + if ((i == (j*8)) && ((i != (k*16)))) + { + if (read(i) < 0x0F) + { + sLog.outDebugInLine("| 0%X ", read(i) ); + } + else + { + sLog.outDebugInLine("| %X ", read(i) ); + } + ++j; + } + else if (i == (k*16)) + { + if (read(i) < 0x0F) + { + sLog.outDebugInLine("\n"); + if(sLog.IsIncludeTime()) + sLog.outDebugInLine(" "); + + sLog.outDebugInLine("0%X ", read(i) ); + } + else + { + sLog.outDebugInLine("\n"); + if(sLog.IsIncludeTime()) + sLog.outDebugInLine(" "); + + sLog.outDebugInLine("%X ", read(i) ); + } + + ++k; + ++j; + } + else + { + if (read(i) < 0x0F) + { + sLog.outDebugInLine("0%X ", read(i) ); + } + else + { + sLog.outDebugInLine("%X ", read(i) ); + } + } + } + sLog.outDebugInLine("\n"); + } + + protected: + bool PrintPosError(bool add, size_t pos, size_t esize) const + { + sLog.outError("ERROR: Attempt %s in ByteBuffer (pos: %u size: %u) value with size: %u",(add ? "put" : "get"),pos, size(), esize); + + // assert must fail after function call + return false; + } + + size_t _rpos, _wpos; + std::vector _storage; +}; + +template ByteBuffer &operator<<(ByteBuffer &b, std::vector v) +{ + b << (uint32)v.size(); + for (typename std::vector::iterator i = v.begin(); i != v.end(); i++) + { + b << *i; + } + return b; +} + +template ByteBuffer &operator>>(ByteBuffer &b, std::vector &v) +{ + uint32 vsize; + b >> vsize; + v.clear(); + while(vsize--) + { + T t; + b >> t; + v.push_back(t); + } + return b; +} + +template ByteBuffer &operator<<(ByteBuffer &b, std::list v) +{ + b << (uint32)v.size(); + for (typename std::list::iterator i = v.begin(); i != v.end(); i++) + { + b << *i; + } + return b; +} + +template ByteBuffer &operator>>(ByteBuffer &b, std::list &v) +{ + uint32 vsize; + b >> vsize; + v.clear(); + while(vsize--) + { + T t; + b >> t; + v.push_back(t); + } + return b; +} + +template ByteBuffer &operator<<(ByteBuffer &b, std::map &m) +{ + b << (uint32)m.size(); + for (typename std::map::iterator i = m.begin(); i != m.end(); i++) + { + b << i->first << i->second; + } + return b; +} + +template ByteBuffer &operator>>(ByteBuffer &b, std::map &m) +{ + uint32 msize; + b >> msize; + m.clear(); + while(msize--) + { + K k; + V v; + b >> k >> v; + m.insert(make_pair(k, v)); + } + return b; +} +#endif diff --git a/src/shared/Common.cpp b/src/shared/Common.cpp new file mode 100644 index 00000000000..d64404a6e91 --- /dev/null +++ b/src/shared/Common.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Common.h" + +char const* localeNames[MAX_LOCALE] = { + "enUS", + "koKR", + "frFR", + "deDE", + "zhCN", + "zhTW", + "esES", + "esMX", + "ruRU" +}; + +LocaleConstant GetLocaleByName(std::string name) +{ + for(uint32 i = 0; i < MAX_LOCALE; ++i) + if(name==localeNames[i]) + return LocaleConstant(i); + + return LOCALE_enUS; // including enGB case +} diff --git a/src/shared/Common.h b/src/shared/Common.h new file mode 100644 index 00000000000..4e93ad71eb2 --- /dev/null +++ b/src/shared/Common.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef MANGOSSERVER_COMMON_H +#define MANGOSSERVER_COMMON_H + +#include "Platform/Define.h" + +#if COMPILER == COMPILER_MICROSOFT + +#pragma warning(disable:4996) + +#ifndef __SHOW_STUPID_WARNINGS__ + +#pragma warning(disable:4244) + +#pragma warning(disable:4267) + +#pragma warning(disable:4800) + +#pragma warning(disable:4018) + +#pragma warning(disable:4311) + +#pragma warning(disable:4305) + +#pragma warning(disable:4005) +#endif // __SHOW_STUPID_WARNINGS__ +#endif // __GNUC__ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "Utilities/HashMap.h" +#include +#include +#include +#include +#include +#include +#include + +#if PLATFORM == PLATFORM_WINDOWS +#define STRCASECMP stricmp +#else +#define STRCASECMP strcasecmp +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if PLATFORM == PLATFORM_WINDOWS +# define FD_SETSIZE 1024 +# include +// XP winver - needed to compile with standard leak check in MemoryLeaks.h +// uncomment later if needed +//#define _WIN32_WINNT 0x0501 +# include +//#undef WIN32_WINNT +#else +# include +# include +# include +# include +# include +# include +#endif + +#if COMPILER == COMPILER_MICROSOFT + +#include + +#define I64FMT "%016I64X" +#define I64FMTD "%I64u" +#define SI64FMTD "%I64d" +#define snprintf _snprintf +#define atoll __atoi64 +#define vsnprintf _vsnprintf +#define strdup _strdup +#define finite(X) _finite(X) + +#else + +#define stricmp strcasecmp +#define strnicmp strncasecmp +#define I64FMT "%016llX" +#define I64FMTD "%llu" +#define SI64FMTD "%lld" +#endif + +inline float finiteAlways(float f) { return finite(f) ? f : 0.0f; } + +#define atol(a) strtoul( a, NULL, 10) + +#define STRINGIZE(a) #a + +enum TimeConstants +{ + MINUTE = 60, + HOUR = MINUTE*60, + DAY = HOUR*24, + MONTH = DAY*30 +}; + +enum AccountTypes +{ + SEC_PLAYER = 0, + SEC_MODERATOR = 1, + SEC_GAMEMASTER = 2, + SEC_ADMINISTRATOR = 3 +}; + +enum LocaleConstant +{ + LOCALE_enUS = 0, + LOCALE_koKR = 1, + LOCALE_frFR = 2, + LOCALE_deDE = 3, + LOCALE_zhCN = 4, + LOCALE_zhTW = 5, + LOCALE_esES = 6, + LOCALE_esMX = 7, + LOCALE_ruRU = 8 +}; + +#define MAX_LOCALE 9 + +extern char const* localeNames[MAX_LOCALE]; + +LocaleConstant GetLocaleByName(std::string name); + +// we always use stdlibc++ std::max/std::min, undefine some not C++ standard defines (Win API and some pother platforms) +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#endif diff --git a/src/shared/Config/Config.cpp b/src/shared/Config/Config.cpp new file mode 100644 index 00000000000..292cf390276 --- /dev/null +++ b/src/shared/Config/Config.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ConfigEnv.h" +#include "Policies/SingletonImp.h" + +INSTANTIATE_SINGLETON_1(Config); + +Config::Config() : mIgnoreCase(true), mConf(NULL) +{ +} + + +Config::~Config() +{ + delete mConf; +} + + +bool Config::SetSource(const char *file, bool ignorecase) +{ + mIgnoreCase = ignorecase; + mFilename = file; + + return Reload(); +} + +bool Config::Reload() +{ + delete mConf; + + mConf = new DOTCONFDocument(mIgnoreCase ? + DOTCONFDocument::CASEINSENSETIVE : + DOTCONFDocument::CASESENSETIVE); + + if (mConf->setContent(mFilename.c_str()) == -1) + { + delete mConf; + mConf = NULL; + return false; + } + + return true; +} + +bool Config::GetString(const char* name, std::string *value) +{ + if(!mConf) + return false; + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return false; + + *value = node->getValue(); + + return true; +} + +bool Config::GetString(const char* name, char const **value) +{ + if(!mConf) + return false; + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return false; + + *value = node->getValue(); + + return true; +} + + +std::string Config::GetStringDefault(const char* name, const char* def) +{ + if(!mConf) + return std::string(def); + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return std::string(def); + + return std::string(node->getValue()); +} + + +bool Config::GetBool(const char* name, bool *value) +{ + if(!mConf) + return false; + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return false; + + const char* str = node->getValue(); + if(strcmp(str, "true") == 0 || strcmp(str, "TRUE") == 0 || + strcmp(str, "yes") == 0 || strcmp(str, "YES") == 0 || + strcmp(str, "1") == 0) + { + *value = true; + } + else + *value = false; + + return true; +} + + +bool Config::GetBoolDefault(const char* name, const bool def) +{ + bool val; + return GetBool(name, &val) ? val : def; +} + + +bool Config::GetInt(const char* name, int *value) +{ + if(!mConf) + return false; + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return false; + + *value = atoi(node->getValue()); + + return true; +} + + +bool Config::GetFloat(const char* name, float *value) +{ + if(!mConf) + return false; + + DOTCONFDocumentNode const *node = mConf->findNode(name); + if(!node || !node->getValue()) + return false; + + *value = atof(node->getValue()); + + return true; +} + + +int Config::GetIntDefault(const char* name, const int def) +{ + int val; + return GetInt(name, &val) ? val : def; +} + + +float Config::GetFloatDefault(const char* name, const float def) +{ + float val; + return (GetFloat(name, &val) ? val : def); +} diff --git a/src/shared/Config/Config.h b/src/shared/Config/Config.h new file mode 100644 index 00000000000..bc753f6d33a --- /dev/null +++ b/src/shared/Config/Config.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include "Platform/Define.h" + +class DOTCONFDocument; + +class MANGOS_DLL_SPEC Config +{ + public: + Config(); + ~Config(); + + bool SetSource(const char *file, bool ignorecase = true); + bool Reload(); + + bool GetString(const char* name, std::string *value); + bool GetString(const char* name, char const **value); + std::string GetStringDefault(const char* name, const char* def); + + bool GetBool(const char* name, bool *value); + bool GetBoolDefault(const char* name, const bool def = false); + + bool GetInt(const char* name, int *value); + int GetIntDefault(const char* name, const int def); + + bool GetFloat(const char* name, float *value); + float GetFloatDefault(const char* name, const float def); + + std::string GetFilename() const { return mFilename; } + private: + std::string mFilename; + bool mIgnoreCase; + DOTCONFDocument *mConf; +}; + +#define sConfig MaNGOS::Singleton::Instance() + +#endif diff --git a/src/shared/Config/ConfigEnv.h b/src/shared/Config/ConfigEnv.h new file mode 100644 index 00000000000..7ef06f015a2 --- /dev/null +++ b/src/shared/Config/ConfigEnv.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#if !defined(CONFIGENVIRONMENT_H) + +#define CONFIGENVIRONMENT_H + +#include "Common.h" +#include "dotconfpp/dotconfpp.h" +#include "Config.h" +#include "Log.h" + +#endif diff --git a/src/shared/Config/ConfigLibrary.vcproj b/src/shared/Config/ConfigLibrary.vcproj new file mode 100644 index 00000000000..a8f1fc322e7 --- /dev/null +++ b/src/shared/Config/ConfigLibrary.vcproj @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/shared/Config/Makefile.am b/src/shared/Config/Makefile.am new file mode 100644 index 00000000000..e36f80a2fc4 --- /dev/null +++ b/src/shared/Config/Makefile.am @@ -0,0 +1,40 @@ +# Copyright (C) 2005-2008 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +## Process this file with automake to produce Makefile.in + +## Sub-directories to parse + +## CPP flags for includes, defines, etc. +AM_CPPFLAGS = $(MYSQL_INCLUDES) $(POSTGRE_INCLUDES) -I$(top_builddir)/src/shared -I$(srcdir) -I$(srcdir)/../../../dep/include -I$(srcdir)/../../framework -I$(srcdir)/../../shared -I$(srcdir)/../../../dep/include/g3dlite + +## Build MaNGOS shared library and its parts as convenience library. +# All libraries will be convenience libraries. Might be changed to shared +# later. +noinst_LIBRARIES = libmangosconfig.a + +libmangosconfig_a_SOURCES = \ + dotconfpp/dotconfpp.cpp \ + dotconfpp/dotconfpp.h \ + dotconfpp/mempool.cpp \ + dotconfpp/mempool.h \ + Config.cpp \ + Config.h \ + ConfigEnv.h + +# VC++ project workspace for dotconfpp +EXTRA_DIST = \ + ConfigLibrary.vcproj diff --git a/src/shared/Config/dotconfpp/dotconfpp.cpp b/src/shared/Config/dotconfpp/dotconfpp.cpp new file mode 100644 index 00000000000..60008747ad1 --- /dev/null +++ b/src/shared/Config/dotconfpp/dotconfpp.cpp @@ -0,0 +1,583 @@ + +#include "Common.h" + +#include "dotconfpp.h" + +#if !defined(R_OK) +#define R_OK 04 +#endif + +DOTCONFDocumentNode::DOTCONFDocumentNode():previousNode(NULL), nextNode(NULL), parentNode(NULL), childNode(NULL), + values(NULL), valuesCount(0), + name(NULL), lineNum(0), fileName(NULL), closed(true) +{ +} + +DOTCONFDocumentNode::~DOTCONFDocumentNode() +{ + free(name); + if(values != NULL){ + for(int i = 0 ; i < valuesCount; i++){ + free(values[i]); + } + free(values); + } +} + +void DOTCONFDocumentNode::pushValue(char * _value) +{ + ++valuesCount; + values = (char**)realloc(values, valuesCount*sizeof(char*)); + values[valuesCount-1] = strdup(_value); +} + +const char* DOTCONFDocumentNode::getValue(int index) const +{ + if(index >= valuesCount){ + return NULL; + } + return values[index]; +} + +DOTCONFDocument::DOTCONFDocument(DOTCONFDocument::CaseSensitive caseSensitivity): + mempool(NULL), + curParent(NULL), curPrev(NULL), curLine(0), file(NULL), fileName(NULL) +{ + if(caseSensitivity == CASESENSETIVE){ + cmp_func = strcmp; + } else { + cmp_func = strcasecmp; + } + + mempool = new AsyncDNSMemPool(1024); + mempool->initialize(); +} + +DOTCONFDocument::~DOTCONFDocument() +{ + for(std::list::iterator i = nodeTree.begin(); i != nodeTree.end(); i++){ + delete(*i); + } + for(std::list::iterator i = requiredOptions.begin(); i != requiredOptions.end(); i++){ + free(*i); + } + for(std::list::iterator i = processedFiles.begin(); i != processedFiles.end(); i++){ + free(*i); + } + free(fileName); + delete mempool; +} + +int DOTCONFDocument::cleanupLine(char * line) +{ + char * start = line; + char * bg = line; + bool multiline = false; + bool concat = false; + char * word = NULL; + + if(!words.empty() && quoted) + concat = true; + + while(*line){ + if((*line == '#' || *line == ';') && !quoted){ + *bg = 0; + if(strlen(start)){ + + if(concat){ + word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1); + strcpy(word, words.back()); + strcat(word, start); + words.pop_back(); + concat = false; + } else { + word = mempool->strdup(start); + } + words.push_back(word); + } + break; + } + if(*line == '=' && !quoted){ + *line = ' ';continue; + } + + // Allowing \" in there causes problems with directory paths + // like "C:\MaNGOS\" + //if(*line == '\\' && (*(line+1) == '"' || *(line+1) == '\'')){ + if(*line == '\\' && (*(line+1) == '\'')) { + *bg++ = *(line+1); + line+=2; continue; + } + if(*line == '\\' && *(line+1) == 'n'){ + *bg++ = '\n'; + line+=2; continue; + } + if(*line == '\\' && *(line+1) == 'r'){ + *bg++ = '\r'; + line+=2; continue; + } + if(*line == '\\' && (*(line+1) == '\n' || *(line+1) == '\r')){ + *bg = 0; + if(strlen(start)){ + + if(concat){ + word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1); + strcpy(word, words.back()); + strcat(word, start); + words.pop_back(); + concat = false; + } else { + word = mempool->strdup(start); + } + words.push_back(word); + } + multiline = true; + break; + } + if(*line == '"' || *line == '\''){ + quoted = !quoted; + ++line; continue; + } + if(isspace(*line) && !quoted){ + *bg++ = 0; + if(strlen(start)){ + + if(concat){ + word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1); + strcpy(word, words.back()); + strcat(word, start); + words.pop_back(); + concat = false; + } else { + word = mempool->strdup(start); + } + words.push_back(word); + } + start = bg; + while(isspace(*++line)) {} + + continue; + } + *bg++ = *line++; + } + + if(quoted && !multiline){ + error(curLine, fileName, "unterminated quote"); + return -1; + } + + return multiline?1:0; +} + +int DOTCONFDocument::parseLine() +{ + char * word = NULL; + char * nodeName = NULL; + char * nodeValue = NULL; + DOTCONFDocumentNode * tagNode = NULL; + bool newNode = false; + + for(std::list::iterator i = words.begin(); i != words.end(); i++) { + word = *i; + + if(*word == '<'){ + newNode = true; + } + + if(newNode){ + nodeValue = NULL; + nodeName = NULL; + newNode = false; + } + + size_t wordLen = strlen(word); + if(word[wordLen-1] == '>'){ + word[wordLen-1] = 0; + newNode = true; + } + + if(nodeName == NULL){ + nodeName = word; + bool closed = true; + if(*nodeName == '<'){ + if(*(nodeName+1) != '/'){ + ++nodeName; + closed = false; + } else { + nodeName+=2; + std::list::reverse_iterator itr=nodeTree.rbegin(); + for(; itr!=nodeTree.rend(); ++itr){ + if(!cmp_func(nodeName, (*itr)->name) && !(*itr)->closed){ + (*itr)->closed = true; + curParent = (*itr)->parentNode; + curPrev = *itr; + break; + } + } + if(itr==nodeTree.rend()){ + error(curLine, fileName, "not matched closing tag ", nodeName); + return -1; + } + continue; + } + } + tagNode = new DOTCONFDocumentNode; + tagNode->name = strdup(nodeName); + tagNode->document = this; + tagNode->fileName = processedFiles.back(); + tagNode->lineNum = curLine; + tagNode->closed = closed; + if(!nodeTree.empty()){ + DOTCONFDocumentNode * prev = nodeTree.back(); + if(prev->closed){ + + curPrev->nextNode = tagNode; + tagNode->previousNode = curPrev; + tagNode->parentNode = curParent; + + } else { + prev->childNode = tagNode; + tagNode->parentNode = prev; + curParent = prev; + } + } + nodeTree.push_back(tagNode); + curPrev = tagNode; + } else { + nodeValue = word; + tagNode->pushValue(nodeValue); + } + } + + return 0; +} +int DOTCONFDocument::parseFile(DOTCONFDocumentNode * _parent) +{ + char str[512]; + int ret = 0; + curLine = 0; + curParent = _parent; + + quoted = false; + size_t slen = 0; + + while(fgets(str, 511, file)){ + ++curLine; + slen = strlen(str); + if( slen >= 510 ){ + error(curLine, fileName, "warning: line too long"); + } + if(str[slen-1] != '\n'){ + str[slen] = '\n'; + str[slen+1] = 0; + } + if((ret = cleanupLine(str)) == -1){ + break; + } + if(ret == 0){ + if(!words.empty()){ + ret = parseLine(); + mempool->free(); + words.clear(); + if(ret == -1){ + break; + } + } + } + } + + return ret; +} + +int DOTCONFDocument::checkConfig(const std::list::iterator & from) +{ + int ret = 0; + + DOTCONFDocumentNode * tagNode = NULL; + int vi = 0; + for(std::list::iterator i = from; i != nodeTree.end(); i++){ + tagNode = *i; + if(!tagNode->closed){ + error(tagNode->lineNum, tagNode->fileName, "unclosed tag %s", tagNode->name); + ret = -1; + break; + } + vi = 0; + while( vi < tagNode->valuesCount ){ + + if(strstr(tagNode->values[vi], "${") && strchr(tagNode->values[vi], '}') ){ + ret = macroSubstitute(tagNode, vi ); + mempool->free(); + if(ret == -1){ + break; + } + } + ++vi; + } + if(ret == -1){ + break; + } + } + + return ret; +} + +int DOTCONFDocument::setContent(const char * _fileName) +{ + int ret = 0; + char realpathBuf[PATH_MAX]; + + if(realpath(_fileName, realpathBuf) == NULL){ + error(0, NULL, "realpath(%s) failed: %s", _fileName, strerror(errno)); + return -1; + } + + fileName = strdup(realpathBuf); + + processedFiles.push_back(strdup(realpathBuf)); + + if(( file = fopen(fileName, "r")) == NULL){ + error(0, NULL, "failed to open file '%s': %s", fileName, strerror(errno)); + return -1; + } + + ret = parseFile(); + + (void) fclose(file); + + if(!ret){ + + if( (ret = checkConfig(nodeTree.begin())) == -1){ + return -1; + } + + std::list::iterator from; + DOTCONFDocumentNode * tagNode = NULL; + int vi = 0; + for(std::list::iterator i = nodeTree.begin(); i!=nodeTree.end(); i++){ + tagNode = *i; + if(!cmp_func("DOTCONFPPIncludeFile", tagNode->name)){ + vi = 0; + while( vi < tagNode->valuesCount ){ + if(access(tagNode->values[vi], R_OK) == -1){ + error(tagNode->lineNum, tagNode->fileName, "%s: %s", tagNode->values[vi], strerror(errno)); + return -1; + } + if(realpath(tagNode->values[vi], realpathBuf) == NULL){ + error(tagNode->lineNum, tagNode->fileName, "realpath(%s) failed: %s", tagNode->values[vi], strerror(errno)); + return -1; + } + + bool processed = false; + for(std::list::const_iterator itInode = processedFiles.begin(); itInode != processedFiles.end(); itInode++){ + if(!strcmp(*itInode, realpathBuf)){ + processed = true; + break; + } + } + if(processed){ + break; + } + + processedFiles.push_back(strdup(realpathBuf)); + + file = fopen(tagNode->values[vi], "r"); + if(file == NULL){ + error(tagNode->lineNum, fileName, "failed to open file '%s': %s", tagNode->values[vi], strerror(errno)); + return -1; + } + + fileName = strdup(realpathBuf); + from = nodeTree.end(); --from; + + ret = parseFile(); + (void) fclose(file); + if(ret == -1) + return -1; + if(checkConfig(++from) == -1){ + return -1; + } + ++vi; + } + } + } + + + if(!requiredOptions.empty()) + ret = checkRequiredOptions(); + } + + return ret; +} + +int DOTCONFDocument::checkRequiredOptions() +{ + for(std::list::const_iterator ci = requiredOptions.begin(); ci != requiredOptions.end(); ci++){ + bool matched = false; + for(std::list::iterator i = nodeTree.begin(); i!=nodeTree.end(); i++){ + if(!cmp_func((*i)->name, *ci)){ + matched = true; + break; + } + } + if(!matched){ + error(0, NULL, "required option '%s' not specified", *ci); + return -1; + } + } + return 0; +} + +void DOTCONFDocument::error(int lineNum, const char * fileName_, const char * fmt, ...) +{ + va_list args; + va_start(args, fmt); + + size_t len = (lineNum!=0?strlen(fileName_):0) + strlen(fmt) + 50; + char * buf = (char*)mempool->alloc(len); + + if(lineNum) + (void) snprintf(buf, len, "DOTCONF++: file '%s', line %d: %s\n", fileName_, lineNum, fmt); + else + (void) snprintf(buf, len, "DOTCONF++: %s\n", fmt); + + (void) vfprintf(stderr, buf, args); + + va_end(args); +} + +char * DOTCONFDocument::getSubstitution(char * macro, int lineNum) +{ + char * buf = NULL; + char * variable = macro+2; + + char * endBr = strchr(macro, '}'); + + if(!endBr){ + error(lineNum, fileName, "unterminated '{'"); + return NULL; + } + *endBr = 0; + + char * defaultValue = strchr(variable, ':'); + + if(defaultValue){ + *defaultValue++ = 0; + if(*defaultValue != '-'){ + error(lineNum, fileName, "incorrect macro substitution syntax"); + return NULL; + } + ++defaultValue; + if(*defaultValue == '"' || *defaultValue == '\''){ + ++defaultValue; + defaultValue[strlen(defaultValue)-1] = 0; + } + } else { + defaultValue = NULL; + } + + char * subs = getenv(variable); + if( subs ){ + buf = mempool->strdup(subs); + } else { + std::list::iterator i = nodeTree.begin(); + DOTCONFDocumentNode * tagNode = NULL; + for(; i!=nodeTree.end(); i++){ + tagNode = *i; + if(!cmp_func(tagNode->name, variable)){ + if(tagNode->valuesCount != 0){ + buf = mempool->strdup(tagNode->values[0]); + break; + } + } + } + if( i == nodeTree.end() ){ + if( defaultValue ){ + buf = mempool->strdup(defaultValue); + } else { + error(lineNum, fileName, "substitution not found and default value not given"); + return NULL; + } + } + } + return buf; +} + +int DOTCONFDocument::macroSubstitute(DOTCONFDocumentNode * tagNode, int valueIndex) +{ + int ret = 0; + char * macro = tagNode->values[valueIndex]; + size_t valueLen = strlen(tagNode->values[valueIndex])+1; + char * value = (char*)mempool->alloc(valueLen); + char * v = value; + char * subs = NULL; + + while(*macro){ + if(*macro == '$' && *(macro+1) == '{'){ + char * m = strchr(macro, '}'); + subs = getSubstitution(macro, tagNode->lineNum); + if(subs == NULL){ + ret = -1; + break; + } + macro = m + 1; + *v = 0; + v = (char*)mempool->alloc(strlen(value)+strlen(subs)+valueLen); + strcpy(v, value); + value = strcat(v, subs); + v = value + strlen(value); + continue; + } + *v++ = *macro++; + } + *v = 0; + + free(tagNode->values[valueIndex]); + tagNode->values[valueIndex] = strdup(value); + return ret; +} + +const DOTCONFDocumentNode * DOTCONFDocument::getFirstNode() const +{ + if ( !nodeTree.empty() ) { + return *nodeTree.begin(); + } else { + return NULL; + } +} + +const DOTCONFDocumentNode * DOTCONFDocument::findNode(const char * nodeName, const DOTCONFDocumentNode * parentNode, const DOTCONFDocumentNode * startNode) const +{ + + + std::list::const_iterator i = nodeTree.begin(); + + if(startNode == NULL) + startNode = parentNode; + + if(startNode != NULL){ + while( i != nodeTree.end() && (*i) != startNode ){ + ++i; + } + if( i != nodeTree.end() ) ++i; + } + + for(; i!=nodeTree.end(); i++){ + + if((*i)->parentNode != parentNode){ + continue; + } + if(!cmp_func(nodeName, (*i)->name)){ + return *i; + } + } + return NULL; +} + +void DOTCONFDocument::setRequiredOptionNames(const char ** requiredOptionNames) +{ + while(*requiredOptionNames){ + requiredOptions.push_back(strdup( *requiredOptionNames )); + ++requiredOptionNames; + } +} diff --git a/src/shared/Config/dotconfpp/dotconfpp.h b/src/shared/Config/dotconfpp/dotconfpp.h new file mode 100644 index 00000000000..15c4f7fcd78 --- /dev/null +++ b/src/shared/Config/dotconfpp/dotconfpp.h @@ -0,0 +1,110 @@ + + + +#ifndef DOTCONFPP_H +#define DOTCONFPP_H + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef WIN32 +#define PATH_MAX _MAX_PATH +#define snprintf _snprintf +#define strcasecmp stricmp +#define realpath(path,resolved_path) _fullpath(resolved_path, path, _MAX_PATH) +#include +#else +#include +#include +#include +#include +#endif + +#include "mempool.h" + +class DOTCONFDocument; + +class DOTCONFDocumentNode +{ +friend class DOTCONFDocument; +private: + DOTCONFDocumentNode * previousNode; + DOTCONFDocumentNode * nextNode; + DOTCONFDocumentNode * parentNode; + DOTCONFDocumentNode * childNode; + char ** values; + int valuesCount; + char * name; + const DOTCONFDocument * document; + int lineNum; + char * fileName; + bool closed; + + void pushValue(char * _value); + +public: + DOTCONFDocumentNode(); + ~DOTCONFDocumentNode(); + + const char * getConfigurationFileName()const { return fileName; } + int getConfigurationLineNumber() const { return lineNum; } + + const DOTCONFDocumentNode * getNextNode() const { return nextNode; } + const DOTCONFDocumentNode * getPreviuosNode() const { return previousNode; } + const DOTCONFDocumentNode * getParentNode() const { return parentNode; } + const DOTCONFDocumentNode * getChildNode() const { return childNode; } + const char* getValue(int index = 0) const; + const char * getName() const { return name; } + const DOTCONFDocument * getDocument() const { return document; } +}; + +class DOTCONFDocument +{ +public: + enum CaseSensitive { CASESENSETIVE, CASEINSENSETIVE }; +protected: + AsyncDNSMemPool * mempool; +private: + DOTCONFDocumentNode * curParent; + DOTCONFDocumentNode * curPrev; + int curLine; + bool quoted; + std::list nodeTree; + std::list requiredOptions; + std::list processedFiles; + FILE * file; + char * fileName; + std::list words; + int (*cmp_func)(const char *, const char *); + + int checkRequiredOptions(); + int parseLine(); + int parseFile(DOTCONFDocumentNode * _parent = NULL); + int checkConfig(const std::list::iterator & from); + int cleanupLine(char * line); + char * getSubstitution(char * macro, int lineNum); + int macroSubstitute(DOTCONFDocumentNode * tagNode, int valueIndex); + +protected: + virtual void error(int lineNum, const char * fileName, const char * fmt, ...) ATTR_PRINTF(4,5); + +public: + DOTCONFDocument(CaseSensitive caseSensitivity = CASESENSETIVE); + virtual ~DOTCONFDocument(); + + int setContent(const char * _fileName); + + void setRequiredOptionNames(const char ** requiredOptionNames); + const DOTCONFDocumentNode * getFirstNode() const; + const DOTCONFDocumentNode * findNode(const char * nodeName, const DOTCONFDocumentNode * parentNode = NULL, const DOTCONFDocumentNode * startNode = NULL) const; +}; + +#endif diff --git a/src/shared/Config/dotconfpp/mempool.cpp b/src/shared/Config/dotconfpp/mempool.cpp new file mode 100644 index 00000000000..cf589ffb2bc --- /dev/null +++ b/src/shared/Config/dotconfpp/mempool.cpp @@ -0,0 +1,100 @@ + + + +#include "mempool.h" + +AsyncDNSMemPool::PoolChunk::PoolChunk(size_t _size): + pool(NULL), pos(0), size(_size) +{ + pool = ::malloc(size); +} + +AsyncDNSMemPool::PoolChunk::~PoolChunk() +{ + ::free(pool); +} + +AsyncDNSMemPool::AsyncDNSMemPool(size_t _defaultSize): + chunks(NULL), chunksCount(0), defaultSize(_defaultSize), + poolUsage(0), poolUsageCounter(0) +{ +} + +AsyncDNSMemPool::~AsyncDNSMemPool() +{ + for(size_t i = 0; isize - chunk->pos) >= size){ + chunk->pos += size; + return ((char*)chunk->pool) + chunk->pos - size; + } + } + addNewChunk(size); + chunks[chunksCount-1]->pos = size; + return chunks[chunksCount-1]->pool; +} + +void AsyncDNSMemPool::free() +{ + size_t pu = 0; + size_t psz = 0; + ++poolUsageCounter; + + for(size_t i = 0; ipos; + psz += chunks[i]->size; + chunks[i]->pos = 0; + } + poolUsage=(poolUsage>pu)?poolUsage:pu; + + if(poolUsageCounter >= 10 && chunksCount > 1){ + psz -= chunks[chunksCount-1]->size; + if(poolUsage < psz){ + --chunksCount; + delete chunks[chunksCount]; + } + poolUsage = 0; + poolUsageCounter = 0; + } +} + +void * AsyncDNSMemPool::calloc(size_t size) +{ + return ::memset(this->alloc(size), 0, size); +} + +char * AsyncDNSMemPool::strdup(const char *str) +{ + return ::strcpy((char*)this->alloc(strlen(str)+1), str); +} diff --git a/src/shared/Config/dotconfpp/mempool.h b/src/shared/Config/dotconfpp/mempool.h new file mode 100644 index 00000000000..04bd1e006ad --- /dev/null +++ b/src/shared/Config/dotconfpp/mempool.h @@ -0,0 +1,46 @@ + + + +#ifndef ASYNC_DNS_MEMPOOL_H +#define ASYNC_DNS_MEMPOOL_H + +#include +#include +#include + +#undef free +#undef calloc +#undef strdup + +class AsyncDNSMemPool +{ +private: + struct PoolChunk { + void * pool; + size_t pos; + size_t size; + + PoolChunk(size_t _size); + ~PoolChunk(); + }; + PoolChunk ** chunks; + size_t chunksCount; + size_t defaultSize; + + size_t poolUsage; + size_t poolUsageCounter; + + void addNewChunk(size_t size); + +public: + AsyncDNSMemPool(size_t _defaultSize = 4096); + virtual ~AsyncDNSMemPool(); + + int initialize(); + void free(); + void * alloc(size_t size); + void * calloc(size_t size); + char * strdup(const char *str); +}; + +#endif diff --git a/src/shared/Database/DBCStores.cpp b/src/shared/Database/DBCStores.cpp new file mode 100644 index 00000000000..623b9652fb3 --- /dev/null +++ b/src/shared/Database/DBCStores.cpp @@ -0,0 +1,642 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DBCStores.h" +//#include "DataStore.h" +#include "Policies/SingletonImp.h" +#include "Log.h" +#include "ProgressBar.h" + +#include "DBCfmt.cpp" + +#include + +typedef std::map AreaFlagByAreaID; +typedef std::map AreaFlagByMapID; + +DBCStorage sAreaStore(AreaTableEntryfmt); +static AreaFlagByAreaID sAreaFlagByAreaID; +static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files + +DBCStorage sAreaTriggerStore(AreaTriggerEntryfmt); +DBCStorage sBankBagSlotPricesStore(BankBagSlotPricesEntryfmt); +DBCStorage sBattlemasterListStore(BattlemasterListEntryfmt); +DBCStorage sCharTitlesStore(CharTitlesEntryfmt); +DBCStorage sChatChannelsStore(ChatChannelsEntryfmt); +DBCStorage sChrClassesStore(ChrClassesEntryfmt); +DBCStorage sChrRacesStore(ChrRacesEntryfmt); +DBCStorage sCreatureDisplayInfoStore(CreatureDisplayInfofmt); +DBCStorage sCreatureFamilyStore(CreatureFamilyfmt); +DBCStorage sCreatureSpellDataStore(CreatureSpellDatafmt); + +DBCStorage sDurabilityQualityStore(DurabilityQualityfmt); +DBCStorage sDurabilityCostsStore(DurabilityCostsfmt); + +DBCStorage sEmotesTextStore(EmoteEntryfmt); + +typedef std::map FactionTeamMap; +static FactionTeamMap sFactionTeamMap; +DBCStorage sFactionStore(FactionEntryfmt); +DBCStorage sFactionTemplateStore(FactionTemplateEntryfmt); + +DBCStorage sGemPropertiesStore(GemPropertiesEntryfmt); + +DBCStorage sGtCombatRatingsStore(GtCombatRatingsfmt); +DBCStorage sGtChanceToMeleeCritBaseStore(GtChanceToMeleeCritBasefmt); +DBCStorage sGtChanceToMeleeCritStore(GtChanceToMeleeCritfmt); +DBCStorage sGtChanceToSpellCritBaseStore(GtChanceToSpellCritBasefmt); +DBCStorage sGtChanceToSpellCritStore(GtChanceToSpellCritfmt); +DBCStorage sGtOCTRegenHPStore(GtOCTRegenHPfmt); +//DBCStorage sGtOCTRegenMPStore(GtOCTRegenMPfmt); -- not used currently +DBCStorage sGtRegenHPPerSptStore(GtRegenHPPerSptfmt); +DBCStorage sGtRegenMPPerSptStore(GtRegenMPPerSptfmt); +DBCStorage sItemStore(Itemfmt); +//DBCStorage sItemCondExtCostsStore(ItemCondExtCostsEntryfmt); +//DBCStorage sItemDisplayInfoStore(ItemDisplayTemplateEntryfmt); -- not used currently +DBCStorage sItemExtendedCostStore(ItemExtendedCostEntryfmt); +DBCStorage sItemRandomPropertiesStore(ItemRandomPropertiesfmt); +DBCStorage sItemRandomSuffixStore(ItemRandomSuffixfmt); +DBCStorage sItemSetStore(ItemSetEntryfmt); + +DBCStorage sLockStore(LockEntryfmt); + +DBCStorage sMailTemplateStore(MailTemplateEntryfmt); +DBCStorage sMapStore(MapEntryfmt); + +DBCStorage sQuestSortStore(QuestSortEntryfmt); + +DBCStorage sRandomPropertiesPointsStore(RandomPropertiesPointsfmt); + +DBCStorage sSkillLineStore(SkillLinefmt); +DBCStorage sSkillLineAbilityStore(SkillLineAbilityfmt); + +DBCStorage sSoundEntriesStore(SoundEntriesfmt); + +DBCStorage sSpellItemEnchantmentStore(SpellItemEnchantmentfmt); +DBCStorage sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt); +DBCStorage sSpellStore(SpellEntryfmt); +SpellCategoryStore sSpellCategoryStore; +PetFamilySpellsStore sPetFamilySpellsStore; + +DBCStorage sSpellCastTimesStore(SpellCastTimefmt); +DBCStorage sSpellDurationStore(SpellDurationfmt); +DBCStorage sSpellFocusObjectStore(SpellFocusObjectfmt); +DBCStorage sSpellRadiusStore(SpellRadiusfmt); +DBCStorage sSpellRangeStore(SpellRangefmt); +DBCStorage sSpellShapeshiftStore(SpellShapeshiftfmt); +DBCStorage sStableSlotPricesStore(StableSlotPricesfmt); +DBCStorage sTalentStore(TalentEntryfmt); +TalentSpellPosMap sTalentSpellPosMap; +DBCStorage sTalentTabStore(TalentTabEntryfmt); + +// store absolute bit position for first rank for talent inspect +typedef std::map TalentInspectMap; +static TalentInspectMap sTalentPosInInspect; +static TalentInspectMap sTalentTabSizeInInspect; +static uint32 sTalentTabPages[12/*MAX_CLASSES*/][3]; + +DBCStorage sTaxiNodesStore(TaxiNodesEntryfmt); +TaxiMask sTaxiNodesMask; + +// DBC used only for initialization sTaxiPathSetBySource at startup. +TaxiPathSetBySource sTaxiPathSetBySource; +DBCStorage sTaxiPathStore(TaxiPathEntryfmt); + +// DBC used only for initialization sTaxiPathSetBySource at startup. +TaxiPathNodesByPath sTaxiPathNodesByPath; +struct TaxiPathNodeEntry +{ + uint32 path; + uint32 index; + uint32 mapid; + float x; + float y; + float z; + uint32 actionFlag; + uint32 delay; +}; +static DBCStorage sTaxiPathNodeStore(TaxiPathNodeEntryfmt); + +DBCStorage sTotemCategoryStore(TotemCategoryEntryfmt); + +DBCStorage sWorldMapAreaStore(WorldMapAreaEntryfmt); +DBCStorage sWorldSafeLocsStore(WorldSafeLocsEntryfmt); + +typedef std::list StoreProblemList; + +static bool LoadDBC_assert_print(uint32 fsize,uint32 rsize, std::string filename) +{ + sLog.outError("ERROR: Size of '%s' setted by format string (%u) not equal size of C++ structure (%u).",filename.c_str(),fsize,rsize); + + // assert must fail after function call + return false; +} + +template +inline void LoadDBC(uint32& availableDbcLocales,barGoLink& bar, StoreProblemList& errlist, DBCStorage& storage, std::string dbc_path, std::string filename) +{ + // compatibility format and C++ structure sizes + assert(DBCFile::GetFormatRecordSize(storage.GetFormat()) == sizeof(T) || LoadDBC_assert_print(DBCFile::GetFormatRecordSize(storage.GetFormat()),sizeof(T),filename)); + + std::string dbc_filename = dbc_path + filename; + if(storage.Load(dbc_filename.c_str())) + { + bar.step(); + for(uint8 i = 0; i < MAX_LOCALE; ++i) + { + if(!(availableDbcLocales & (1 << i))) + continue; + + std::string dbc_filename_loc = dbc_path + localeNames[i] + "/" + filename; + if(!storage.LoadStringsFrom(dbc_filename_loc.c_str())) + availableDbcLocales &= ~(1<DBC records + sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID),area->exploreFlag)); + + // fill MapId->DBC records ( skip sub zones and continents ) + if(area->zone==0 && area->mapid != 0 && area->mapid != 1 && area->mapid != 530 ) + sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid,area->exploreFlag)); + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaTriggerStore, dbcPath,"AreaTrigger.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBankBagSlotPricesStore, dbcPath,"BankBagSlotPrices.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBattlemasterListStore, dbcPath,"BattlemasterList.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCharTitlesStore, dbcPath,"CharTitles.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChatChannelsStore, dbcPath,"ChatChannels.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrClassesStore, dbcPath,"ChrClasses.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sChrRacesStore, dbcPath,"ChrRaces.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureDisplayInfoStore, dbcPath,"CreatureDisplayInfo.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureFamilyStore, dbcPath,"CreatureFamily.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCreatureSpellDataStore, dbcPath,"CreatureSpellData.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityCostsStore, dbcPath,"DurabilityCosts.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sDurabilityQualityStore, dbcPath,"DurabilityQuality.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sEmotesTextStore, dbcPath,"EmotesText.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionStore, dbcPath,"Faction.dbc"); + for (uint32 i=0;iteam) + { + SimpleFactionsList &flist = sFactionTeamMap[faction->team]; + flist.push_back(i); + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionTemplateStore, dbcPath,"FactionTemplate.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGemPropertiesStore, dbcPath,"GemProperties.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtCombatRatingsStore, dbcPath,"gtCombatRatings.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToMeleeCritBaseStore, dbcPath,"gtChanceToMeleeCritBase.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToMeleeCritStore, dbcPath,"gtChanceToMeleeCrit.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToSpellCritBaseStore, dbcPath,"gtChanceToSpellCritBase.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtChanceToSpellCritStore, dbcPath,"gtChanceToSpellCrit.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtOCTRegenHPStore, dbcPath,"gtOCTRegenHP.dbc"); + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtOCTRegenMPStore, dbcPath,"gtOCTRegenMP.dbc"); -- not used currently + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtRegenHPPerSptStore, dbcPath,"gtRegenHPPerSpt.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtRegenMPPerSptStore, dbcPath,"gtRegenMPPerSpt.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemStore, dbcPath,"Item.dbc"); + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemDisplayInfoStore, dbcPath,"ItemDisplayInfo.dbc"); -- not used currently + //LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemCondExtCostsStore, dbcPath,"ItemCondExtCosts.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemExtendedCostStore, dbcPath,"ItemExtendedCost.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemRandomPropertiesStore,dbcPath,"ItemRandomProperties.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemRandomSuffixStore, dbcPath,"ItemRandomSuffix.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sItemSetStore, dbcPath,"ItemSet.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sLockStore, dbcPath,"Lock.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMailTemplateStore, dbcPath,"MailTemplate.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMapStore, dbcPath,"Map.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sQuestSortStore, dbcPath,"QuestSort.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sRandomPropertiesPointsStore, dbcPath,"RandPropPoints.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSkillLineStore, dbcPath,"SkillLine.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSkillLineAbilityStore, dbcPath,"SkillLineAbility.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSoundEntriesStore, dbcPath,"SoundEntries.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellStore, dbcPath,"Spell.dbc"); + for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) + { + SpellEntry const * spell = sSpellStore.LookupEntry(i); + if(spell && spell->Category) + sSpellCategoryStore[spell->Category].insert(i); + + // DBC not support uint64 fields but SpellEntry have SpellFamilyFlags mapped at 2 uint32 fields + // uint32 field already converted to bigendian if need, but must be swapped for correct uint64 bigendian view + #if MANGOS_ENDIAN == MANGOS_BIGENDIAN + std::swap(*((uint32*)(&spell->SpellFamilyFlags)),*(((uint32*)(&spell->SpellFamilyFlags))+1)); + #endif + } + + for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) + { + SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j); + + if(!skillLine) + continue; + + SpellEntry const* spellInfo = sSpellStore.LookupEntry(skillLine->spellId); + + if(spellInfo && (spellInfo->Attributes & 0x1D0) == 0x1D0) + { + for (unsigned int i = 1; i < sCreatureFamilyStore.GetNumRows(); ++i) + { + CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(i); + if(!cFamily) + continue; + + if(skillLine->skillId != cFamily->skillLine && skillLine->skillId != cFamily->skillLine2) + continue; + + sPetFamilySpellsStore[i].insert(spellInfo->Id); + } + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellCastTimesStore, dbcPath,"SpellCastTimes.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellDurationStore, dbcPath,"SpellDuration.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellFocusObjectStore, dbcPath,"SpellFocusObject.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellItemEnchantmentStore,dbcPath,"SpellItemEnchantment.dbc"); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellItemEnchantmentConditionStore,dbcPath,"SpellItemEnchantmentCondition.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRadiusStore, dbcPath,"SpellRadius.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellRangeStore, dbcPath,"SpellRange.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSpellShapeshiftStore, dbcPath,"SpellShapeshiftForm.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sStableSlotPricesStore, dbcPath,"StableSlotPrices.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentStore, dbcPath,"Talent.dbc"); + + // create talent spells set + for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) continue; + for (int j = 0; j < 5; j++) + if(talentInfo->RankID[j]) + sTalentSpellPosMap[talentInfo->RankID[j]] = TalentSpellPos(i,j); + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentTabStore, dbcPath,"TalentTab.dbc"); + + // preper fast data access to bit pos of talent ranks for use at inspecting + { + // fill table by amount of talent ranks and fill sTalentTabBitSizeInInspect + // store in with (row,col,talent)->size key for correct sorting by (row,col) + typedef std::map TalentBitSize; + TalentBitSize sTalentBitSize; + for(uint32 i = 1; i < sTalentStore.GetNumRows(); ++i) + { + TalentEntry const *talentInfo = sTalentStore.LookupEntry(i); + if (!talentInfo) continue; + + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab ); + if(!talentTabInfo) + continue; + + // find talent rank + uint32 curtalent_maxrank = 0; + for(uint32 k = 5; k > 0; --k) + { + if(talentInfo->RankID[k-1]) + { + curtalent_maxrank = k; + break; + } + } + + sTalentBitSize[(talentInfo->Row<<24) + (talentInfo->Col<<16)+talentInfo->TalentID] = curtalent_maxrank; + sTalentTabSizeInInspect[talentInfo->TalentTab] += curtalent_maxrank; + } + + // now have all max ranks (and then bit amount used for store talent ranks in inspect) + for(uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId) + { + TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentTabId ); + if(!talentTabInfo) + continue; + + // store class talent tab pages + uint32 cls = 1; + for(uint32 m=1;!(m & talentTabInfo->ClassMask) && cls < 12 /*MAX_CLASSES*/;m <<=1, ++cls) {} + + sTalentTabPages[cls][talentTabInfo->tabpage]=talentTabId; + + // add total amount bits for first rank starting from talent tab first talent rank pos. + uint32 pos = 0; + for(TalentBitSize::iterator itr = sTalentBitSize.begin(); itr != sTalentBitSize.end(); ++itr) + { + uint32 talentId = itr->first & 0xFFFF; + TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentId ); + if(!talentInfo) + continue; + + if(talentInfo->TalentTab != talentTabId) + continue; + + sTalentPosInInspect[talentId] = pos; + pos+= itr->second; + } + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiNodesStore, dbcPath,"TaxiNodes.dbc"); + + // Initialize global taxinodes mask + memset(sTaxiNodesMask,0,sizeof(sTaxiNodesMask)); + for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i) + { + if(sTaxiNodesStore.LookupEntry(i)) + { + uint8 field = (uint8)((i - 1) / 32); + uint32 submask = 1<<((i-1)%32); + sTaxiNodesMask[field] |= submask; + } + } + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiPathStore, dbcPath,"TaxiPath.dbc"); + for(uint32 i = 1; i < sTaxiPathStore.GetNumRows(); ++i) + if(TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(i)) + sTaxiPathSetBySource[entry->from][entry->to] = TaxiPathBySourceAndDestination(entry->ID,entry->price); + uint32 pathCount = sTaxiPathStore.GetNumRows(); + + //## TaxiPathNode.dbc ## Loaded only for initialization different structures + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiPathNodeStore, dbcPath,"TaxiPathNode.dbc"); + // Calculate path nodes count + std::vector pathLength; + pathLength.resize(pathCount); // 0 and some other indexes not used + for(uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i) + if(TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i)) + ++pathLength[entry->path]; + // Set path length + sTaxiPathNodesByPath.resize(pathCount); // 0 and some other indexes not used + for(uint32 i = 1; i < sTaxiPathNodesByPath.size(); ++i) + sTaxiPathNodesByPath[i].resize(pathLength[i]); + // fill data + for(uint32 i = 1; i < sTaxiPathNodeStore.GetNumRows(); ++i) + if(TaxiPathNodeEntry const* entry = sTaxiPathNodeStore.LookupEntry(i)) + sTaxiPathNodesByPath[entry->path][entry->index] = TaxiPathNode(entry->mapid,entry->x,entry->y,entry->z,entry->actionFlag,entry->delay); + sTaxiPathNodeStore.Clear(); + + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTotemCategoryStore, dbcPath,"TotemCategory.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapAreaStore, dbcPath,"WorldMapArea.dbc"); + LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldSafeLocsStore, dbcPath,"WorldSafeLocs.dbc"); + + // error checks + if(bad_dbc_files.size() >= DBCFilesCount ) + { + sLog.outError("\nIncorrect DataDir value in mangosd.conf or ALL required *.dbc files (%d) not found by path: %sdbc",DBCFilesCount,dataPath.c_str()); + exit(1); + } + else if(!bad_dbc_files.empty() ) + { + std::string str; + for(std::list::iterator i = bad_dbc_files.begin(); i != bad_dbc_files.end(); ++i) + str += *i + "\n"; + + sLog.outError("\nSome required *.dbc files (%u from %d) not found or not compatible:\n%s",bad_dbc_files.size(),DBCFilesCount,str.c_str()); + exit(1); + } + + // check at up-to-date DBC files (53085 is last added spell in 2.4.3) + // check at up-to-date DBC files (17514 is last ID in SkillLineAbilities in 2.4.3) + // check at up-to-date DBC files (598 is last map added in 2.4.3) + // check at up-to-date DBC files (1127 is last gem property added in 2.4.3) + // check at up-to-date DBC files (2425 is last item extended cost added in 2.4.3) + // check at up-to-date DBC files (71 is last char title added in 2.4.3) + // check at up-to-date DBC files (1768 is last area added in 2.4.3) + if( !sSpellStore.LookupEntry(53085) || + !sSkillLineAbilityStore.LookupEntry(17514) || + !sMapStore.LookupEntry(598) || + !sGemPropertiesStore.LookupEntry(1127) || + !sItemExtendedCostStore.LookupEntry(2425) || + !sCharTitlesStore.LookupEntry(71) || + !sAreaStore.LookupEntry(1768) ) + { + sLog.outError("\nYou have _outdated_ DBC files. Please extract correct versions from current using client."); + exit(1); + } + + sLog.outString(); + sLog.outString( ">> Loaded %d data stores", DBCFilesCount ); + sLog.outString(); +} + +SimpleFactionsList const* GetFactionTeamList(uint32 faction) +{ + FactionTeamMap::const_iterator itr = sFactionTeamMap.find(faction); + if(itr==sFactionTeamMap.end()) + return NULL; + return &itr->second; +} + +char* GetPetName(uint32 petfamily, uint32 dbclang) +{ + if(!petfamily) + return NULL; + CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(petfamily); + if(!pet_family) + return NULL; + return pet_family->Name[dbclang]?pet_family->Name[dbclang]:NULL; +} + +TalentSpellPos const* GetTalentSpellPos(uint32 spellId) +{ + TalentSpellPosMap::const_iterator itr = sTalentSpellPosMap.find(spellId); + if(itr==sTalentSpellPosMap.end()) + return NULL; + + return &itr->second; +} + +uint32 GetTalentSpellCost(uint32 spellId) +{ + if(TalentSpellPos const* pos = GetTalentSpellPos(spellId)) + return pos->rank+1; + + return 0; +} + +AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id) +{ + AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id); + if(i == sAreaFlagByAreaID.end()) + return NULL; + + return sAreaStore.LookupEntry(i->second); +} + +AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id) +{ + if(area_flag) + return sAreaStore.LookupEntry(area_flag); + + if(MapEntry const* mapEntry = sMapStore.LookupEntry(map_id)) + return GetAreaEntryByAreaID(mapEntry->linked_zone); + + return NULL; +} + +uint32 GetAreaFlagByMapId(uint32 mapid) +{ + AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid); + if(i == sAreaFlagByMapID.end()) + return 0; + else + return i->second; +} + +uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId) +{ + if(mapid != 530) // speed for most cases + return mapid; + + if(WorldMapAreaEntry const* wma = sWorldMapAreaStore.LookupEntry(zoneId)) + return wma->virtual_map_id >= 0 ? wma->virtual_map_id : wma->map_id; + + return mapid; +} + +ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId) +{ + mapid = GetVirtualMapForMapAndZone(mapid,zoneId); + if(mapid < 2) + return CONTENT_1_60; + + MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); + return (!mapEntry || !mapEntry->IsExpansionMap()) ? CONTENT_1_60 : CONTENT_61_70; +} + +ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id) +{ + // not sorted, numbering index from 0 + for(uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) + { + ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(i); + if(ch && ch->ChannelID == channel_id) + return ch; + } + return NULL; +} + +bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId) +{ + if(requiredTotemCategoryId==0) + return true; + if(itemTotemCategoryId==0) + return false; + + TotemCategoryEntry const* itemEntry = sTotemCategoryStore.LookupEntry(itemTotemCategoryId); + if(!itemEntry) + return false; + TotemCategoryEntry const* reqEntry = sTotemCategoryStore.LookupEntry(requiredTotemCategoryId); + if(!reqEntry) + return false; + + if(itemEntry->categoryType!=reqEntry->categoryType) + return false; + + return (itemEntry->categoryMask & reqEntry->categoryMask)==reqEntry->categoryMask; +} + +void Zone2MapCoordinates(float& x,float& y,uint32 zone) +{ + WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); + + // if not listed then map coordinates (instance) + if(!maEntry) + return; + + std::swap(x,y); // at client map coords swapped + x = x*((maEntry->x2-maEntry->x1)/100)+maEntry->x1; + y = y*((maEntry->y2-maEntry->y1)/100)+maEntry->y1; // client y coord from top to down +} + +void Map2ZoneCoordinates(float& x,float& y,uint32 zone) +{ + WorldMapAreaEntry const* maEntry = sWorldMapAreaStore.LookupEntry(zone); + + // if not listed then map coordinates (instance) + if(!maEntry) + return; + + x = (x-maEntry->x1)/((maEntry->x2-maEntry->x1)/100); + y = (y-maEntry->y1)/((maEntry->y2-maEntry->y1)/100); // client y coord from top to down + std::swap(x,y); // client have map coords swapped +} + +uint32 GetTalentInspectBitPosInTab(uint32 talentId) +{ + TalentInspectMap::const_iterator itr = sTalentPosInInspect.find(talentId); + if(itr == sTalentPosInInspect.end()) + return 0; + + return itr->second; +} + +uint32 GetTalentTabInspectBitSize(uint32 talentTabId) +{ + TalentInspectMap::const_iterator itr = sTalentTabSizeInInspect.find(talentTabId); + if(itr == sTalentTabSizeInInspect.end()) + return 0; + + return itr->second; +} + +uint32 const* GetTalentTabPages(uint32 cls) +{ + return sTalentTabPages[cls]; +} + +// script support functions +MANGOS_DLL_SPEC DBCStorage const* GetSoundEntriesStore() { return &sSoundEntriesStore; } +MANGOS_DLL_SPEC DBCStorage const* GetSpellStore() { return &sSpellStore; } +MANGOS_DLL_SPEC DBCStorage const* GetSpellRangeStore() { return &sSpellRangeStore; } diff --git a/src/shared/Database/DBCStores.h b/src/shared/Database/DBCStores.h new file mode 100644 index 00000000000..e1d05298695 --- /dev/null +++ b/src/shared/Database/DBCStores.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DBCSTORES_H +#define DBCSTORES_H + +#include "Common.h" +//#include "DataStore.h" +#include "dbcfile.h" +#include "DBCStructure.h" + +#include + +typedef std::list SimpleFactionsList; + +SimpleFactionsList const* GetFactionTeamList(uint32 faction); +char* GetPetName(uint32 petfamily, uint32 dbclang); +uint32 GetTalentSpellCost(uint32 spellId); +TalentSpellPos const* GetTalentSpellPos(uint32 spellId); + +AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id); +AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag,uint32 map_id); +uint32 GetAreaFlagByMapId(uint32 mapid); + +uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId); + +enum ContentLevels +{ + CONTENT_1_60 = 0, + CONTENT_61_70 +}; +ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId); + +ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id); + +bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId); + +void Zone2MapCoordinates(float& x,float& y,uint32 zone); +void Map2ZoneCoordinates(float& x,float& y,uint32 zone); + +uint32 GetTalentInspectBitPosInTab(uint32 talentId); +uint32 GetTalentTabInspectBitSize(uint32 talentTabId); +uint32 const* /*[3]*/ GetTalentTabPages(uint32 cls); + +template +class DBCStorage +{ + typedef std::list StringPoolList; + public: + explicit DBCStorage(const char *f) : nCount(0), fieldCount(0), fmt(f), indexTable(NULL), m_dataTable(NULL) { } + ~DBCStorage() { Clear(); } + + T const* LookupEntry(uint32 id) const { return (id>=nCount)?NULL:indexTable[id]; } + uint32 GetNumRows() const { return nCount; } + char const* GetFormat() const { return fmt; } + uint32 GetFieldCount() const { return fieldCount; } + + bool Load(char const* fn) + { + + DBCFile dbc; + // Check if load was sucessful, only then continue + if(!dbc.Load(fn, fmt)) + return false; + + fieldCount = dbc.GetCols(); + m_dataTable = (T*)dbc.AutoProduceData(fmt,nCount,(char**&)indexTable); + m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + + // error in dbc file at loading if NULL + return indexTable!=NULL; + } + + bool LoadStringsFrom(char const* fn) + { + // DBC must be already loaded using Load + if(!indexTable) + return false; + + DBCFile dbc; + // Check if load was successful, only then continue + if(!dbc.Load(fn, fmt)) + return false; + + m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + + return true; + } + + void Clear() + { + if (!indexTable) + return; + + delete[] ((char*)indexTable); + indexTable = NULL; + delete[] ((char*)m_dataTable); + m_dataTable = NULL; + + while(!m_stringPoolList.empty()) + { + delete[] m_stringPoolList.front(); + m_stringPoolList.pop_front(); + } + nCount = 0; + } + + private: + uint32 nCount; + uint32 fieldCount; + char const* fmt; + T** indexTable; + T* m_dataTable; + StringPoolList m_stringPoolList; +}; + +extern DBCStorage sAreaStore;// recommend access using functions +extern DBCStorage sAreaTriggerStore; +extern DBCStorage sBankBagSlotPricesStore; +extern DBCStorage sBattlemasterListStore; +//extern DBCStorage sChatChannelsStore; -- accessed using function, no usable index +extern DBCStorage sCharTitlesStore; +extern DBCStorage sChrClassesStore; +extern DBCStorage sChrRacesStore; +extern DBCStorage sCreatureDisplayInfoStore; +extern DBCStorage sCreatureFamilyStore; +extern DBCStorage sCreatureSpellDataStore; +extern DBCStorage sDurabilityCostsStore; +extern DBCStorage sDurabilityQualityStore; +extern DBCStorage sEmotesTextStore; +extern DBCStorage sFactionStore; +extern DBCStorage sFactionTemplateStore; +extern DBCStorage sGemPropertiesStore; + +extern DBCStorage sGtCombatRatingsStore; +extern DBCStorage sGtChanceToMeleeCritBaseStore; +extern DBCStorage sGtChanceToMeleeCritStore; +extern DBCStorage sGtChanceToSpellCritBaseStore; +extern DBCStorage sGtChanceToSpellCritStore; +extern DBCStorage sGtOCTRegenHPStore; +//extern DBCStorage sGtOCTRegenMPStore; -- not used currently +extern DBCStorage sGtRegenHPPerSptStore; +extern DBCStorage sGtRegenMPPerSptStore; +extern DBCStorage sItemStore; +//extern DBCStorage sItemDisplayInfoStore; -- not used currently +extern DBCStorage sItemExtendedCostStore; +extern DBCStorage sItemRandomPropertiesStore; +extern DBCStorage sItemRandomSuffixStore; +extern DBCStorage sItemSetStore; +extern DBCStorage sLockStore; +extern DBCStorage sMailTemplateStore; +extern DBCStorage sMapStore; +extern DBCStorage sQuestSortStore; +extern DBCStorage sRandomPropertiesPointsStore; +extern DBCStorage sSkillLineStore; +extern DBCStorage sSkillLineAbilityStore; +extern DBCStorage sSoundEntriesStore; +extern DBCStorage sSpellCastTimesStore; +extern DBCStorage sSpellDurationStore; +extern DBCStorage sSpellFocusObjectStore; +extern DBCStorage sSpellItemEnchantmentStore; +extern DBCStorage sSpellItemEnchantmentConditionStore; +extern SpellCategoryStore sSpellCategoryStore; +extern PetFamilySpellsStore sPetFamilySpellsStore; +extern DBCStorage sSpellRadiusStore; +extern DBCStorage sSpellRangeStore; +extern DBCStorage sSpellShapeshiftStore; +extern DBCStorage sSpellStore; +extern DBCStorage sStableSlotPricesStore; +extern DBCStorage sTalentStore; +extern DBCStorage sTalentTabStore; +extern DBCStorage sTaxiNodesStore; +extern DBCStorage sTaxiPathStore; +extern TaxiMask sTaxiNodesMask; +extern TaxiPathSetBySource sTaxiPathSetBySource; +extern TaxiPathNodesByPath sTaxiPathNodesByPath; +extern DBCStorage sTotemCategoryStore; +//extern DBCStorage sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates +extern DBCStorage sWorldSafeLocsStore; + +void LoadDBCStores(std::string dataPath); + +// script support functions +MANGOS_DLL_SPEC DBCStorage const* GetSoundEntriesStore(); +MANGOS_DLL_SPEC DBCStorage const* GetSpellStore(); +MANGOS_DLL_SPEC DBCStorage const* GetSpellRangeStore(); +#endif diff --git a/src/shared/Database/DBCStructure.h b/src/shared/Database/DBCStructure.h new file mode 100644 index 00000000000..c0370180511 --- /dev/null +++ b/src/shared/Database/DBCStructure.h @@ -0,0 +1,937 @@ +/* + * Copyright (C) 2005-2008 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DBCSTRUCTURE_H +#define DBCSTRUCTURE_H + +#include "Platform/Define.h" + +#include +#include +#include + +// 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<